From 3ef709771d5cd69d3a4aff65b8b6f09fbfb96655 Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Sun, 5 Apr 2026 17:26:09 -0400 Subject: [PATCH 1/4] first try at web --- README.md | 1 + docs/docs/api.md | 22 + docs/docs/installation.md | 19 +- example/index.web.tsx | 15 + example/package.json | 13 +- example/src/AppWeb.tsx | 377 + example/src/tests/index.ts | 1 + example/src/tests/web.ts | 80 + example/vite.config.ts | 34 + example/web-build/assets/index-DmS4xevg.js | 17 + example/web-build/assets/index-bI9uVmc1.js | 3 + .../web-build/assets/sqlite3-DGXXSD5r.wasm | Bin 0 -> 859730 bytes .../sqlite3-opfs-async-proxy-BWKAW6aw.js | 1 + .../assets/sqlite3-worker1-CEle-zSR.mjs | 15461 +++++++++++++++ .../assets/sqlite3-worker1-jtPPE4oU.js | 3 + example/web-build/index.html | 28 + example/web/index.html | 28 + example/web/index.web.tsx | 1 + src/Storage.web.ts | 82 + src/functions.ts | 15 + src/functions.web.ts | 498 + src/index.web.ts | 24 + src/types.ts | 2 + src/web/sqlite-wasm.d.ts | 3 + src/web/sqlite-wasm/index.mjs | 15645 ++++++++++++++++ .../sqlite-wasm/sqlite3-opfs-async-proxy.js | 666 + src/web/sqlite-wasm/sqlite3-worker1.mjs | 15461 +++++++++++++++ src/web/sqlite-wasm/sqlite3.wasm | Bin 0 -> 859730 bytes yarn.lock | 831 +- 29 files changed, 49316 insertions(+), 15 deletions(-) create mode 100644 example/index.web.tsx create mode 100644 example/src/AppWeb.tsx create mode 100644 example/src/tests/web.ts create mode 100644 example/vite.config.ts create mode 100644 example/web-build/assets/index-DmS4xevg.js create mode 100644 example/web-build/assets/index-bI9uVmc1.js create mode 100644 example/web-build/assets/sqlite3-DGXXSD5r.wasm create mode 100644 example/web-build/assets/sqlite3-opfs-async-proxy-BWKAW6aw.js create mode 100644 example/web-build/assets/sqlite3-worker1-CEle-zSR.mjs create mode 100644 example/web-build/assets/sqlite3-worker1-jtPPE4oU.js create mode 100644 example/web-build/index.html create mode 100644 example/web/index.html create mode 100644 example/web/index.web.tsx create mode 100644 src/Storage.web.ts create mode 100644 src/functions.web.ts create mode 100644 src/index.web.ts create mode 100644 src/web/sqlite-wasm.d.ts create mode 100644 src/web/sqlite-wasm/index.mjs create mode 100644 src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js create mode 100644 src/web/sqlite-wasm/sqlite3-worker1.mjs create mode 100644 src/web/sqlite-wasm/sqlite3.wasm diff --git a/README.md b/README.md index 0a427f75..814c1896 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ https://discord.gg/W9XmqCQCKP Some of the big supported features: +- iOS, Android, macOS and web support - Vanilla sqlite - Libsql is supported as a compilation target - SQLCipher is supported as a compilation target diff --git a/docs/docs/api.md b/docs/docs/api.md index 4882d6e3..ee1c99d3 100644 --- a/docs/docs/api.md +++ b/docs/docs/api.md @@ -16,6 +16,20 @@ export const db = open({ }); ``` +### Open Async (Web) + +On web, opening is async-only and requires OPFS. + +```tsx +import { openAsync } from '@op-engineering/op-sqlite'; + +export const db = await openAsync({ + name: 'myDb.sqlite', +}); +``` + +On web, `open()` intentionally throws. Use `openAsync()`. + ### SQLCipher Open If you are using SQLCipher all the methods are the same with the exception of the open method which needs an extra `encryptionKey` to encrypt/decrypt the database. @@ -51,6 +65,12 @@ try { } ``` +### Web note + +On web, `execute()` runs the full SQL string passed to it. +On native, `execute()` currently runs only the first prepared statement. +If you need identical behavior across platforms, avoid multi-statement SQL strings. + ### Execute with Host Objects It’s possible to return HostObjects when using a query. The benefit is that HostObjects are only created in C++ and only when you try to access a value inside of them a C++ value → JS value conversion happens. This means creation is fast, property access is slow. The use case is clear if you are returning **massive** amount of objects but only displaying/accessing a few of them at the time. @@ -109,6 +129,8 @@ You can do sync queries via the `executeSync` functions. Not available in transa let res = db.executeSync('SELECT 1'); ``` +On web, sync APIs intentionally throw. Use async methods only. + ## Transactions Wraps the code inside in a transaction. Any error thrown inside of the transaction body function will ROLLBACK the transaction. diff --git a/docs/docs/installation.md b/docs/docs/installation.md index 99482299..ecfd5304 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -17,7 +17,24 @@ npx expo install @op-engineering/op-sqlite npx expo prebuild --clean ``` -This package only runs on `iOS`, `Android` and `macOS`. +This package runs on `iOS`, `Android`, `macOS` and `web`. + +## Web requirements + +Web support is async-only and uses the sqlite wasm worker API with OPFS persistence. + +Required runtime behavior on web: + +- Use `openAsync()` to open the database. +- Use async DB methods such as `execute()` and `closeAsync()`. +- Synchronous APIs (for example `open()` and `executeSync()`) intentionally throw on web. +- SQLCipher and libsql are not supported on web. As well as loading extensions. +- OPFS is required for the web backend. If OPFS is unavailable, `openAsync()` throws. + +Required response headers (for worker/OPFS setup): + +- `Cross-Origin-Opener-Policy: same-origin` +- `Cross-Origin-Embedder-Policy: require-corp` # Configuration diff --git a/example/index.web.tsx b/example/index.web.tsx new file mode 100644 index 00000000..d466b7f2 --- /dev/null +++ b/example/index.web.tsx @@ -0,0 +1,15 @@ +import { AppRegistry } from 'react-native'; +import App from './src/AppWeb'; +import { name as appName } from './app.json'; + +AppRegistry.registerComponent(appName, () => App); + +const rootTag = (globalThis as any).document?.getElementById('root'); +if (!rootTag) { + throw new Error('Root element not found'); +} + +AppRegistry.runApplication(appName, { + rootTag, + initialProps: {}, +}); diff --git a/example/package.json b/example/package.json index 3e48179d..3e99b4b9 100644 --- a/example/package.json +++ b/example/package.json @@ -13,7 +13,10 @@ "pods:nuke": "cd ios && rm -rf Pods && rm -rf Podfile.lock && bundle exec pod install", "run:android:release": "cd android && ./gradlew assembleRelease && adb install -r app/build/outputs/apk/release/app-release.apk && adb shell am start -n com.op.sqlite.example/.MainActivity", "build:android": "cd android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a", - "build:ios": "cd ios && xcodebuild -workspace OPSQLiteExample.xcworkspace -scheme debug -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO" + "build:ios": "cd ios && xcodebuild -workspace OPSQLiteExample.xcworkspace -scheme debug -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO", + "web": "vite", + "web:build": "vite build", + "web:preview": "vite preview" }, "dependencies": { "@op-engineering/op-test": "^0.2.5", @@ -21,8 +24,10 @@ "clsx": "^2.0.0", "events": "^3.3.0", "react": "19.1.1", + "react-dom": "19.1.1", "react-native": "0.82.1", - "react-native-safe-area-context": "^5.6.2" + "react-native-safe-area-context": "^5.6.2", + "react-native-web": "^0.21.2" }, "devDependencies": { "@babel/core": "^7.25.2", @@ -36,11 +41,13 @@ "@react-native/typescript-config": "0.81.5", "@types/chance": "^1.1.7", "@types/react": "^19.1.1", + "@vitejs/plugin-react": "^5.1.0", "patch-package": "^8.0.1", "react-native-builder-bob": "^0.40.13", "react-native-monorepo-config": "^0.1.9", "react-native-restart": "^0.0.27", - "tailwindcss": "3.3.2" + "tailwindcss": "3.3.2", + "vite": "^7.1.9" }, "engines": { "node": ">=18" diff --git a/example/src/AppWeb.tsx b/example/src/AppWeb.tsx new file mode 100644 index 00000000..21d4233a --- /dev/null +++ b/example/src/AppWeb.tsx @@ -0,0 +1,377 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'; +import { openAsync } from '@op-engineering/op-sqlite'; + +type TableColumn = { + cid: number; + name: string; + type: string; + notnull: number; + pk: number; +}; + +type Note = { + id: number; + label: string; + created_at: string; +}; + +export default function AppWeb() { + const [status, setStatus] = useState('Initializing sqlite web backend...'); + const [columns, setColumns] = useState([]); + const [notes, setNotes] = useState([]); + const [inputValue, setInputValue] = useState('First note from OPFS web db'); + const [loading, setLoading] = useState(false); + + const withDb = useCallback( + async (work: (db: Awaited>) => Promise) => { + const db = await openAsync({ + name: 'example-web.sqlite', + }); + + try { + return await work(db); + } finally { + await db.closeAsync(); + } + }, + [] + ); + + const refreshTableInfo = useCallback(async () => { + return withDb(async (db) => { + const [schemaResult, rowsResult] = await Promise.all([ + db.execute('PRAGMA table_info(web_notes)'), + db.execute('SELECT id, label, created_at FROM web_notes ORDER BY id DESC LIMIT 20'), + ]); + + setColumns( + schemaResult.rows.map((row) => ({ + cid: Number(row.cid), + name: String(row.name), + type: String(row.type), + notnull: Number(row.notnull), + pk: Number(row.pk), + })) + ); + + setNotes( + rowsResult.rows.map((row) => ({ + id: Number(row.id), + label: String(row.label), + created_at: String(row.created_at), + })) + ); + }); + }, [withDb]); + + const ensureSchema = useCallback(async () => { + return withDb(async (db) => { + await db.execute( + 'CREATE TABLE IF NOT EXISTS web_notes (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT NOT NULL, created_at TEXT NOT NULL)' + ); + }); + }, [withDb]); + + const insertNote = useCallback(async () => { + const value = inputValue.trim(); + if (!value) { + setStatus('Type a value before inserting.'); + return; + } + + setLoading(true); + try { + await withDb(async (db) => { + await db.execute( + 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', + [value, new Date().toISOString()] + ); + }); + + setStatus('Insert succeeded. Data is persisted in OPFS.'); + await refreshTableInfo(); + setInputValue(''); + } catch (error) { + setStatus(`Insert failed: ${(error as Error).message}`); + } finally { + setLoading(false); + } + }, [inputValue, refreshTableInfo, withDb]); + + const insertSampleData = useCallback(async () => { + setLoading(true); + try { + await withDb(async (db) => { + const now = new Date().toISOString(); + + await db.execute( + 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', + ['Sample: OPFS persistence check', now] + ); + await db.execute( + 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', + ['Sample: Query path works', now] + ); + await db.execute( + 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', + ['Sample: closeAsync completed', now] + ); + }); + + setStatus('Sample rows inserted successfully.'); + await refreshTableInfo(); + } catch (error) { + setStatus(`Sample insert failed: ${(error as Error).message}`); + } finally { + setLoading(false); + } + }, [refreshTableInfo, withDb]); + + const clearRows = useCallback(async () => { + setLoading(true); + try { + await withDb(async (db) => { + await db.execute('DELETE FROM web_notes'); + }); + + setStatus('Table cleared.'); + await refreshTableInfo(); + } catch (error) { + setStatus(`Clear failed: ${(error as Error).message}`); + } finally { + setLoading(false); + } + }, [refreshTableInfo, withDb]); + + useEffect(() => { + const work = async () => { + try { + await ensureSchema(); + await refreshTableInfo(); + setStatus('SQLite web backend initialized with OPFS persistence.'); + } catch (error) { + setStatus( + `Failed to initialize web sqlite backend: ${(error as Error).message}` + ); + } + }; + + work(); + }, [ensureSchema, refreshTableInfo]); + + return ( + + OP-SQLite Web Example + {status} + + + 1) Insert data + + + + Insert one row + + + Insert sample rows + + + + + + Table: web_notes + + + Refresh table + + + Clear table + + + + Schema + + cid + name + type + nn + pk + + {columns.map((column) => ( + + {column.cid} + {column.name} + {column.type} + {column.notnull} + {column.pk} + + ))} + + Rows ({notes.length}) + + id + label + created_at + + {notes.length === 0 ? ( + No rows in web_notes. + ) : ( + notes.map((entry) => ( + + {entry.id} + {entry.label} + {entry.created_at} + + )) + )} + + + ); +} + +const styles = StyleSheet.create({ + container: { + minHeight: '100%', + padding: 24, + backgroundColor: '#101418', + gap: 12, + }, + title: { + color: '#f9fafb', + fontWeight: '700', + fontSize: 24, + marginBottom: 2, + }, + status: { + color: '#9ca3af', + marginBottom: 8, + }, + panel: { + borderWidth: 1, + borderColor: '#273043', + borderRadius: 12, + backgroundColor: '#151a22', + padding: 12, + gap: 8, + }, + panelTitle: { + color: '#f3f4f6', + fontWeight: '600', + fontSize: 16, + }, + sectionLabel: { + color: '#cbd5e1', + marginTop: 4, + marginBottom: 2, + fontWeight: '600', + }, + row: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }, + input: { + borderWidth: 1, + borderColor: '#334155', + borderRadius: 10, + color: '#f8fafc', + paddingHorizontal: 10, + paddingVertical: 9, + backgroundColor: '#0f172a', + }, + button: { + backgroundColor: '#2563eb', + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 9, + }, + buttonSecondary: { + backgroundColor: '#0ea5e9', + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 9, + }, + buttonDanger: { + backgroundColor: '#dc2626', + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 9, + }, + buttonText: { + color: '#f8fafc', + fontWeight: '600', + }, + empty: { + color: '#9ca3af', + fontStyle: 'italic', + }, + tableHeader: { + flexDirection: 'row', + borderBottomWidth: 1, + borderBottomColor: '#334155', + paddingBottom: 6, + marginTop: 4, + }, + tableRow: { + flexDirection: 'row', + borderBottomWidth: 1, + borderBottomColor: '#1f2937', + paddingVertical: 8, + }, + headerCell: { + color: '#93c5fd', + fontWeight: '700', + fontSize: 12, + }, + rowCell: { + color: '#e5e7eb', + fontSize: 13, + }, + cidCell: { + width: 40, + }, + nameCell: { + flex: 1, + paddingRight: 8, + }, + typeCell: { + width: 80, + }, + flagCell: { + width: 32, + }, + dateCell: { + flex: 1, + paddingLeft: 8, + }, + item: { + borderWidth: 1, + borderColor: '#334155', + borderRadius: 10, + padding: 9, + gap: 2, + backgroundColor: '#0f172a', + }, + itemTitle: { + color: '#93c5fd', + fontWeight: '700', + }, + itemText: { + color: '#f1f5f9', + }, + itemMeta: { + color: '#94a3b8', + fontSize: 14, + }, +}); diff --git a/example/src/tests/index.ts b/example/src/tests/index.ts index 0007b7a3..3044ff24 100644 --- a/example/src/tests/index.ts +++ b/example/src/tests/index.ts @@ -7,3 +7,4 @@ import './queries'; import './reactive'; import './storage'; import './tokenizer'; +import './web'; diff --git a/example/src/tests/web.ts b/example/src/tests/web.ts new file mode 100644 index 00000000..927d1101 --- /dev/null +++ b/example/src/tests/web.ts @@ -0,0 +1,80 @@ +import {open, openAsync, type DB} from '@op-engineering/op-sqlite'; +import {describe, expect, it} from '@op-engineering/op-test'; +import {Platform} from 'react-native'; + +describe('Web backend', () => { + if (Platform.OS !== 'web') { + return; + } + + it('opens with openAsync and executes queries', async () => { + const db = await openAsync({ + name: 'web-tests.sqlite', + }); + + await db.execute( + 'CREATE TABLE IF NOT EXISTS WebTests (id INTEGER PRIMARY KEY, value TEXT NOT NULL)', + ); + await db.execute('DELETE FROM WebTests'); + await db.execute('INSERT INTO WebTests (id, value) VALUES (?, ?)', [1, 'ok']); + + const result = await db.execute('SELECT value FROM WebTests WHERE id = ?', [1]); + + expect(result.rows[0]?.value).toEqual('ok'); + + await db.closeAsync(); + }); + + it('open() throws on web', () => { + let didThrow = false; + + try { + open({ + name: 'web-tests.sqlite', + }); + } catch (error) { + didThrow = true; + expect((error as Error).message.includes('async-only')).toEqual(true); + } + + expect(didThrow).toEqual(true); + }); + + it('executeSync() throws on web', async () => { + let db: DB | null = null; + + try { + db = await openAsync({ + name: 'web-tests.sqlite', + }); + + let didThrow = false; + + try { + db.executeSync('SELECT 1'); + } catch (_error) { + didThrow = true; + } + + expect(didThrow).toEqual(true); + } finally { + await db?.closeAsync(); + } + }); + + it('rejects SQLCipher options on web', async () => { + let didThrow = false; + + try { + await openAsync({ + name: 'web-tests-encrypted.sqlite', + encryptionKey: 'not-supported-on-web', + }); + } catch (error) { + didThrow = true; + expect((error as Error).message.includes('SQLCipher')).toEqual(true); + } + + expect(didThrow).toEqual(true); + }); +}); diff --git a/example/vite.config.ts b/example/vite.config.ts new file mode 100644 index 00000000..05a085f6 --- /dev/null +++ b/example/vite.config.ts @@ -0,0 +1,34 @@ +import path from 'path'; +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +const rootDir = __dirname; +const packageRoot = path.resolve(rootDir, '..'); + +export default defineConfig({ + root: path.resolve(rootDir, 'web'), + plugins: [react()], + resolve: { + alias: { + 'react-native': 'react-native-web', + '@op-engineering/op-sqlite': path.resolve(packageRoot, 'src/index.web.ts'), + }, + extensions: ['.web.tsx', '.web.ts', '.tsx', '.ts', '.jsx', '.js', '.mjs', '.json'], + }, + server: { + headers: { + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }, + fs: { + allow: [packageRoot], + }, + }, + optimizeDeps: { + exclude: ['@op-engineering/op-sqlite'], + }, + build: { + outDir: path.resolve(rootDir, 'web-build'), + emptyOutDir: true, + }, +}); diff --git a/example/web-build/assets/index-DmS4xevg.js b/example/web-build/assets/index-DmS4xevg.js new file mode 100644 index 00000000..e9188f71 --- /dev/null +++ b/example/web-build/assets/index-DmS4xevg.js @@ -0,0 +1,17 @@ +(function(){const u=document.createElement("link").relList;if(u&&u.supports&&u.supports("modulepreload"))return;for(const f of document.querySelectorAll('link[rel="modulepreload"]'))c(f);new MutationObserver(f=>{for(const d of f)if(d.type==="childList")for(const h of d.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&c(h)}).observe(document,{childList:!0,subtree:!0});function o(f){const d={};return f.integrity&&(d.integrity=f.integrity),f.referrerPolicy&&(d.referrerPolicy=f.referrerPolicy),f.crossOrigin==="use-credentials"?d.credentials="include":f.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function c(f){if(f.ep)return;f.ep=!0;const d=o(f);fetch(f.href,d)}})();var Cg=r=>r.disabled||Array.isArray(r.accessibilityStates)&&r.accessibilityStates.indexOf("disabled")>-1,Mg={adjustable:"slider",button:"button",header:"heading",image:"img",imagebutton:null,keyboardkey:null,label:null,link:"link",none:"presentation",search:"search",summary:"region",text:null},zm=r=>{var u=r.accessibilityRole,o=r.role,c=o||u;if(c){var f=Mg[c];if(f!==null)return f||c}},Dg={article:"article",banner:"header",blockquote:"blockquote",button:"button",code:"code",complementary:"aside",contentinfo:"footer",deletion:"del",emphasis:"em",figure:"figure",insertion:"ins",form:"form",list:"ul",listitem:"li",main:"main",navigation:"nav",paragraph:"p",region:"section",strong:"strong"},wg={},zg=function(u){u===void 0&&(u=wg);var o=u.role||u.accessibilityRole;if(o==="label")return"label";var c=zm(u);if(c){if(c==="heading"){var f=u.accessibilityLevel||u["aria-level"];return f!=null?"h"+f:"h1"}return Dg[c]}},Nm={isDisabled:Cg,propsToAccessibilityComponent:zg,propsToAriaRole:zm};function Ci(r){"@babel/helpers - typeof";return Ci=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(u){return typeof u}:function(u){return u&&typeof Symbol=="function"&&u.constructor===Symbol&&u!==Symbol.prototype?"symbol":typeof u},Ci(r)}function Ng(r,u){if(Ci(r)!="object"||!r)return r;var o=r[Symbol.toPrimitive];if(o!==void 0){var c=o.call(r,u);if(Ci(c)!="object")return c;throw new TypeError("@@toPrimitive must return a primitive value.")}return(u==="string"?String:Number)(r)}function Bg(r){var u=Ng(r,"string");return Ci(u)=="symbol"?u:u+""}function Hg(r,u,o){return(u=Bg(u))in r?Object.defineProperty(r,u,{value:o,enumerable:!0,configurable:!0,writable:!0}):r[u]=o,r}function uy(r,u){var o=Object.keys(r);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(r);u&&(c=c.filter(function(f){return Object.getOwnPropertyDescriptor(r,f).enumerable})),o.push.apply(o,c)}return o}function Ot(r){for(var u=1;ur+u.charAt(0).toUpperCase()+u.substring(1);Object.keys(cc).forEach(r=>{Ug.forEach(u=>{cc[Lg(u,r)]=cc[r]})});var qg=r=>r==="currentcolor"||r==="currentColor"||r==="inherit"||r.indexOf("var(")===0;function ia(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}function Bm(r){if(Object.prototype.hasOwnProperty.call(r,"__esModule"))return r;var u=r.default;if(typeof u=="function"){var o=function c(){var f=!1;try{f=this instanceof c}catch{}return f?Reflect.construct(u,arguments,this.constructor):u.apply(this,arguments)};o.prototype=u.prototype}else o={};return Object.defineProperty(o,"__esModule",{value:!0}),Object.keys(r).forEach(function(c){var f=Object.getOwnPropertyDescriptor(r,c);Object.defineProperty(o,c,f.get?f:{enumerable:!0,get:function(){return r[c]}})}),o}var bf,cy;function jg(){if(cy)return bf;cy=1;function r(A){if(typeof A=="number")return A>>>0===A&&A>=0&&A<=4294967295?A:null;if(typeof A!="string")return null;const z=R();let w;if(w=z.hex6.exec(A))return parseInt(w[1]+"ff",16)>>>0;const Y=Q(A);return Y??((w=z.rgb.exec(A))?(x(w[1])<<24|x(w[2])<<16|x(w[3])<<8|255)>>>0:(w=z.rgba.exec(A))?w[6]!==void 0?(x(w[6])<<24|x(w[7])<<16|x(w[8])<<8|q(w[9]))>>>0:(x(w[2])<<24|x(w[3])<<16|x(w[4])<<8|q(w[5]))>>>0:(w=z.hex3.exec(A))?parseInt(w[1]+w[1]+w[2]+w[2]+w[3]+w[3]+"ff",16)>>>0:(w=z.hex8.exec(A))?parseInt(w[1],16)>>>0:(w=z.hex4.exec(A))?parseInt(w[1]+w[1]+w[2]+w[2]+w[3]+w[3]+w[4]+w[4],16)>>>0:(w=z.hsl.exec(A))?(o(M(w[1]),L(w[2]),L(w[3]))|255)>>>0:(w=z.hsla.exec(A))?w[6]!==void 0?(o(M(w[6]),L(w[7]),L(w[8]))|q(w[9]))>>>0:(o(M(w[2]),L(w[3]),L(w[4]))|q(w[5]))>>>0:(w=z.hwb.exec(A))?(c(M(w[1]),L(w[2]),L(w[3]))|255)>>>0:null)}function u(A,z,w){return w<0&&(w+=1),w>1&&(w-=1),w<1/6?A+(z-A)*6*w:w<1/2?z:w<2/3?A+(z-A)*(2/3-w)*6:A}function o(A,z,w){const Y=w<.5?w*(1+z):w+z-w*z,J=2*w-Y,j=u(J,Y,A+1/3),G=u(J,Y,A),ne=u(J,Y,A-1/3);return Math.round(j*255)<<24|Math.round(G*255)<<16|Math.round(ne*255)<<8}function c(A,z,w){if(z+w>=1){const G=Math.round(z*255/(z+w));return G<<24|G<<16|G<<8}const Y=u(0,1,A+1/3)*(1-z-w)+z,J=u(0,1,A)*(1-z-w)+z,j=u(0,1,A-1/3)*(1-z-w)+z;return Math.round(Y*255)<<24|Math.round(J*255)<<16|Math.round(j*255)<<8}const f="[-+]?\\d*\\.?\\d+",d=f+"%";function h(...A){return"\\(\\s*("+A.join(")\\s*,?\\s*(")+")\\s*\\)"}function b(...A){return"\\(\\s*("+A.slice(0,A.length-1).join(")\\s*,?\\s*(")+")\\s*/\\s*("+A[A.length-1]+")\\s*\\)"}function S(...A){return"\\(\\s*("+A.join(")\\s*,\\s*(")+")\\s*\\)"}let y;function R(){return y===void 0&&(y={rgb:new RegExp("rgb"+h(f,f,f)),rgba:new RegExp("rgba("+S(f,f,f,f)+"|"+b(f,f,f,f)+")"),hsl:new RegExp("hsl"+h(f,d,d)),hsla:new RegExp("hsla("+S(f,d,d,f)+"|"+b(f,d,d,f)+")"),hwb:new RegExp("hwb"+h(f,d,d)),hex3:/^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex4:/^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#([0-9a-fA-F]{6})$/,hex8:/^#([0-9a-fA-F]{8})$/}),y}function x(A){const z=parseInt(A,10);return z<0?0:z>255?255:z}function M(A){return(parseFloat(A)%360+360)%360/360}function q(A){const z=parseFloat(A);return z<0?0:z>1?255:Math.round(z*255)}function L(A){const z=parseFloat(A);return z<0?0:z>100?1:z/100}function Q(A){switch(A){case"transparent":return 0;case"aliceblue":return 4042850303;case"antiquewhite":return 4209760255;case"aqua":return 16777215;case"aquamarine":return 2147472639;case"azure":return 4043309055;case"beige":return 4126530815;case"bisque":return 4293182719;case"black":return 255;case"blanchedalmond":return 4293643775;case"blue":return 65535;case"blueviolet":return 2318131967;case"brown":return 2771004159;case"burlywood":return 3736635391;case"burntsienna":return 3934150143;case"cadetblue":return 1604231423;case"chartreuse":return 2147418367;case"chocolate":return 3530104575;case"coral":return 4286533887;case"cornflowerblue":return 1687547391;case"cornsilk":return 4294499583;case"crimson":return 3692313855;case"cyan":return 16777215;case"darkblue":return 35839;case"darkcyan":return 9145343;case"darkgoldenrod":return 3095792639;case"darkgray":return 2846468607;case"darkgreen":return 6553855;case"darkgrey":return 2846468607;case"darkkhaki":return 3182914559;case"darkmagenta":return 2332068863;case"darkolivegreen":return 1433087999;case"darkorange":return 4287365375;case"darkorchid":return 2570243327;case"darkred":return 2332033279;case"darksalmon":return 3918953215;case"darkseagreen":return 2411499519;case"darkslateblue":return 1211993087;case"darkslategray":return 793726975;case"darkslategrey":return 793726975;case"darkturquoise":return 13554175;case"darkviolet":return 2483082239;case"deeppink":return 4279538687;case"deepskyblue":return 12582911;case"dimgray":return 1768516095;case"dimgrey":return 1768516095;case"dodgerblue":return 512819199;case"firebrick":return 2988581631;case"floralwhite":return 4294635775;case"forestgreen":return 579543807;case"fuchsia":return 4278255615;case"gainsboro":return 3705462015;case"ghostwhite":return 4177068031;case"gold":return 4292280575;case"goldenrod":return 3668254975;case"gray":return 2155905279;case"green":return 8388863;case"greenyellow":return 2919182335;case"grey":return 2155905279;case"honeydew":return 4043305215;case"hotpink":return 4285117695;case"indianred":return 3445382399;case"indigo":return 1258324735;case"ivory":return 4294963455;case"khaki":return 4041641215;case"lavender":return 3873897215;case"lavenderblush":return 4293981695;case"lawngreen":return 2096890111;case"lemonchiffon":return 4294626815;case"lightblue":return 2916673279;case"lightcoral":return 4034953471;case"lightcyan":return 3774873599;case"lightgoldenrodyellow":return 4210742015;case"lightgray":return 3553874943;case"lightgreen":return 2431553791;case"lightgrey":return 3553874943;case"lightpink":return 4290167295;case"lightsalmon":return 4288707327;case"lightseagreen":return 548580095;case"lightskyblue":return 2278488831;case"lightslategray":return 2005441023;case"lightslategrey":return 2005441023;case"lightsteelblue":return 2965692159;case"lightyellow":return 4294959359;case"lime":return 16711935;case"limegreen":return 852308735;case"linen":return 4210091775;case"magenta":return 4278255615;case"maroon":return 2147483903;case"mediumaquamarine":return 1724754687;case"mediumblue":return 52735;case"mediumorchid":return 3126187007;case"mediumpurple":return 2473647103;case"mediumseagreen":return 1018393087;case"mediumslateblue":return 2070474495;case"mediumspringgreen":return 16423679;case"mediumturquoise":return 1221709055;case"mediumvioletred":return 3340076543;case"midnightblue":return 421097727;case"mintcream":return 4127193855;case"mistyrose":return 4293190143;case"moccasin":return 4293178879;case"navajowhite":return 4292783615;case"navy":return 33023;case"oldlace":return 4260751103;case"olive":return 2155872511;case"olivedrab":return 1804477439;case"orange":return 4289003775;case"orangered":return 4282712319;case"orchid":return 3664828159;case"palegoldenrod":return 4008225535;case"palegreen":return 2566625535;case"paleturquoise":return 2951671551;case"palevioletred":return 3681588223;case"papayawhip":return 4293907967;case"peachpuff":return 4292524543;case"peru":return 3448061951;case"pink":return 4290825215;case"plum":return 3718307327;case"powderblue":return 2967529215;case"purple":return 2147516671;case"rebeccapurple":return 1714657791;case"red":return 4278190335;case"rosybrown":return 3163525119;case"royalblue":return 1097458175;case"saddlebrown":return 2336560127;case"salmon":return 4202722047;case"sandybrown":return 4104413439;case"seagreen":return 780883967;case"seashell":return 4294307583;case"sienna":return 2689740287;case"silver":return 3233857791;case"skyblue":return 2278484991;case"slateblue":return 1784335871;case"slategray":return 1887473919;case"slategrey":return 1887473919;case"snow":return 4294638335;case"springgreen":return 16744447;case"steelblue":return 1182971135;case"tan":return 3535047935;case"teal":return 8421631;case"thistle":return 3636451583;case"tomato":return 4284696575;case"turquoise":return 1088475391;case"violet":return 4001558271;case"wheat":return 4125012991;case"white":return 4294967295;case"whitesmoke":return 4126537215;case"yellow":return 4294902015;case"yellowgreen":return 2597139199}return null}return bf=r,bf}var Yg=jg();const Gg=ia(Yg);var Xg=r=>{if(r==null)return r;var u=Gg(r);if(u!=null)return u=(u<<24|u>>>8)>>>0,u},gs=function(u,o){if(o===void 0&&(o=1),u!=null){if(typeof u=="string"&&qg(u))return u;var c=Xg(u);if(c!=null){var f=c>>16&255,d=c>>8&255,h=c&255,b=(c>>24&255)/255,S=(b*o).toFixed(2);return"rgba("+f+","+d+","+h+","+S+")"}}},Vg={backgroundColor:!0,borderColor:!0,borderTopColor:!0,borderRightColor:!0,borderBottomColor:!0,borderLeftColor:!0,color:!0,shadowColor:!0,textDecorationColor:!0,textShadowColor:!0};function St(r,u){var o=r;return(u==null||!cc[u])&&typeof r=="number"?o=r+"px":u!=null&&Vg[u]&&(o=gs(r)),o}var qt=!!(typeof window<"u"&&window.document&&window.document.createElement),kg={},Qg=!qt||window.CSS!=null&&window.CSS.supports!=null&&(window.CSS.supports("text-decoration-line","none")||window.CSS.supports("-webkit-text-decoration-line","none")),Kg="monospace,monospace",oy='-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif',Zg={borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderBlockColor:["borderTopColor","borderBottomColor"],borderInlineColor:["borderRightColor","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderStyle:["borderTopStyle","borderRightStyle","borderBottomStyle","borderLeftStyle"],borderBlockStyle:["borderTopStyle","borderBottomStyle"],borderInlineStyle:["borderRightStyle","borderLeftStyle"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],borderBlockWidth:["borderTopWidth","borderBottomWidth"],borderInlineWidth:["borderRightWidth","borderLeftWidth"],insetBlock:["top","bottom"],insetInline:["left","right"],marginBlock:["marginTop","marginBottom"],marginInline:["marginRight","marginLeft"],paddingBlock:["paddingTop","paddingBottom"],paddingInline:["paddingRight","paddingLeft"],overflow:["overflowX","overflowY"],overscrollBehavior:["overscrollBehaviorX","overscrollBehaviorY"],borderBlockStartColor:["borderTopColor"],borderBlockStartStyle:["borderTopStyle"],borderBlockStartWidth:["borderTopWidth"],borderBlockEndColor:["borderBottomColor"],borderBlockEndStyle:["borderBottomStyle"],borderBlockEndWidth:["borderBottomWidth"],borderEndStartRadius:["borderBottomLeftRadius"],borderEndEndRadius:["borderBottomRightRadius"],borderStartStartRadius:["borderTopLeftRadius"],borderStartEndRadius:["borderTopRightRadius"],insetBlockEnd:["bottom"],insetBlockStart:["top"],marginBlockStart:["marginTop"],marginBlockEnd:["marginBottom"],paddingBlockStart:["paddingTop"],paddingBlockEnd:["paddingBottom"]},Hm=(r,u)=>{if(!r)return kg;var o={},c=function(){var b=r[f];if(b==null)return"continue";if(f==="backgroundClip")b==="text"&&(o.backgroundClip=b,o.WebkitBackgroundClip=b);else if(f==="flex")b===-1?(o.flexGrow=0,o.flexShrink=1,o.flexBasis="auto"):o.flex=b;else if(f==="font")o[f]=b.replace("System",oy);else if(f==="fontFamily")if(b.indexOf("System")>-1){var S=b.split(/,\s*/);S[S.indexOf("System")]=oy,o[f]=S.join(",")}else b==="monospace"?o[f]=Kg:o[f]=b;else if(f==="textDecorationLine")Qg?o.textDecorationLine=b:o.textDecoration=b;else if(f==="writingDirection")o.direction=b;else{var y=St(r[f],f),R=Zg[f];u&&f==="inset"?(r.insetInline==null&&(o.left=y,o.right=y),r.insetBlock==null&&(o.top=y,o.bottom=y)):u&&f==="margin"?(r.marginInline==null&&(o.marginLeft=y,o.marginRight=y),r.marginBlock==null&&(o.marginTop=y,o.marginBottom=y)):u&&f==="padding"?(r.paddingInline==null&&(o.paddingLeft=y,o.paddingRight=y),r.paddingBlock==null&&(o.paddingTop=y,o.paddingBottom=y)):R?R.forEach((x,M)=>{r[x]==null&&(o[x]=y)}):o[f]=y}};for(var f in r)var d=c();return o};function Wg(r,u){for(var o=r.length,c=u^o,f=0,d;o>=4;)d=r.charCodeAt(f)&255|(r.charCodeAt(++f)&255)<<8|(r.charCodeAt(++f)&255)<<16|(r.charCodeAt(++f)&255)<<24,d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16),d^=d>>>24,d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16),c=(c&65535)*1540483477+(((c>>>16)*1540483477&65535)<<16)^d,o-=4,++f;switch(o){case 3:c^=(r.charCodeAt(f+2)&255)<<16;case 2:c^=(r.charCodeAt(f+1)&255)<<8;case 1:c^=r.charCodeAt(f)&255,c=(c&65535)*1540483477+(((c>>>16)*1540483477&65535)<<16)}return c^=c>>>13,c=(c&65535)*1540483477+(((c>>>16)*1540483477&65535)<<16),c^=c>>>15,c>>>0}var Pg=r=>Wg(r,1).toString(36),Jg=/[A-Z]/g,$g=/^ms-/,Sf={};function Ig(r){return"-"+r.toLowerCase()}function Fg(r){if(r in Sf)return Sf[r];var u=r.replace(Jg,Ig);return Sf[r]=$g.test(u)?"-"+u:u}var Pu={},Ju={},$u={},fy;function Um(){if(fy)return $u;fy=1,Object.defineProperty($u,"__esModule",{value:!0}),$u.default=r;function r(u){return u.charAt(0).toUpperCase()+u.slice(1)}return $u}var sy;function e0(){if(sy)return Ju;sy=1,Object.defineProperty(Ju,"__esModule",{value:!0}),Ju.default=c;var r=Um(),u=o(r);function o(f){return f&&f.__esModule?f:{default:f}}function c(f,d,h){var b=f[d];if(b&&h.hasOwnProperty(d))for(var S=(0,u.default)(d),y=0;y0&&(L[Q]=z)}else{var j=(0,c.default)(M,Q,A,L,x);j&&(L[Q]=j),L=(0,u.default)(x,Q,L)}}return L}}return Pu}var r0=n0();const i0=ia(r0);var tc={};function oc(r){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?oc=function(o){return typeof o}:oc=function(o){return o&&typeof Symbol=="function"&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o},oc(r)}function u0(r){return s0(r)||f0(r)||o0(r)||c0()}function c0(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function o0(r,u){if(r){if(typeof r=="string")return Wf(r,u);var o=Object.prototype.toString.call(r).slice(8,-1);if(o==="Object"&&r.constructor&&(o=r.constructor.name),o==="Map"||o==="Set")return Array.from(o);if(o==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o))return Wf(r,u)}}function f0(r){if(typeof Symbol<"u"&&Symbol.iterator in Object(r))return Array.from(r)}function s0(r){if(Array.isArray(r))return Wf(r)}function Wf(r,u){(u==null||u>r.length)&&(u=r.length);for(var o=0,c=new Array(u);o-1)return c.map(function(b){return h.replace(/image-set\(/g,b+"image-set(")})}return ac}var Y0=j0();const G0=ia(Y0);var lc={},Ty;function X0(){if(Ty)return lc;Ty=1,Object.defineProperty(lc,"__esModule",{value:!0}),lc.default=u;var r={marginBlockStart:["WebkitMarginBefore"],marginBlockEnd:["WebkitMarginAfter"],marginInlineStart:["WebkitMarginStart","MozMarginStart"],marginInlineEnd:["WebkitMarginEnd","MozMarginEnd"],paddingBlockStart:["WebkitPaddingBefore"],paddingBlockEnd:["WebkitPaddingAfter"],paddingInlineStart:["WebkitPaddingStart","MozPaddingStart"],paddingInlineEnd:["WebkitPaddingEnd","MozPaddingEnd"],borderBlockStart:["WebkitBorderBefore"],borderBlockStartColor:["WebkitBorderBeforeColor"],borderBlockStartStyle:["WebkitBorderBeforeStyle"],borderBlockStartWidth:["WebkitBorderBeforeWidth"],borderBlockEnd:["WebkitBorderAfter"],borderBlockEndColor:["WebkitBorderAfterColor"],borderBlockEndStyle:["WebkitBorderAfterStyle"],borderBlockEndWidth:["WebkitBorderAfterWidth"],borderInlineStart:["WebkitBorderStart","MozBorderStart"],borderInlineStartColor:["WebkitBorderStartColor","MozBorderStartColor"],borderInlineStartStyle:["WebkitBorderStartStyle","MozBorderStartStyle"],borderInlineStartWidth:["WebkitBorderStartWidth","MozBorderStartWidth"],borderInlineEnd:["WebkitBorderEnd","MozBorderEnd"],borderInlineEndColor:["WebkitBorderEndColor","MozBorderEndColor"],borderInlineEndStyle:["WebkitBorderEndStyle","MozBorderEndStyle"],borderInlineEndWidth:["WebkitBorderEndWidth","MozBorderEndWidth"]};function u(o,c,f){if(Object.prototype.hasOwnProperty.call(r,o))for(var d=r[o],h=0,b=d.length;h-1&&Y!=="order")for(var J=M[w],j=0,G=J.length;j-1)return A;var z=Q.split(/,(?![^()]*(?:\([^()]*\))?\))/g).filter(function(w){return!/-webkit-|-ms-/.test(w)}).join(",");return x.indexOf("Moz")>-1?z:(q["Webkit"+(0,d.default)(x)]=A,q["Moz"+(0,d.default)(x)]=z,Q)}}return ic}var ep=F0();const tp=ia(ep);var Ne=["Webkit"],ap=["Moz"],lp=["Webkit","Moz"],Xe=["Webkit","ms"],np=["Webkit","Moz","ms"];const rp={plugins:[q0,G0,k0,Z0,J0,tp],prefixMap:{appearance:np,userSelect:lp,textEmphasisPosition:Xe,textEmphasis:Xe,textEmphasisStyle:Xe,textEmphasisColor:Xe,boxDecorationBreak:Xe,clipPath:Ne,maskImage:Xe,maskMode:Xe,maskRepeat:Xe,maskPosition:Xe,maskClip:Xe,maskOrigin:Xe,maskSize:Xe,maskComposite:Xe,mask:Xe,maskBorderSource:Xe,maskBorderMode:Xe,maskBorderSlice:Xe,maskBorderWidth:Xe,maskBorderOutset:Xe,maskBorderRepeat:Xe,maskBorder:Xe,maskType:Xe,textDecorationStyle:Ne,textDecorationSkip:Ne,textDecorationLine:Ne,textDecorationColor:Ne,filter:Ne,breakAfter:Ne,breakBefore:Ne,breakInside:Ne,columnCount:Ne,columnFill:Ne,columnGap:Ne,columnRule:Ne,columnRuleColor:Ne,columnRuleStyle:Ne,columnRuleWidth:Ne,columns:Ne,columnSpan:Ne,columnWidth:Ne,backdropFilter:Ne,hyphens:Ne,flowInto:Ne,flowFrom:Ne,regionFragment:Ne,textOrientation:Ne,tabSize:ap,fontKerning:Ne,textSizeAdjust:Ne}};var ip=i0(rp),up=["animationKeyframes"],Cy=new Map,cp={},op=1,fp=3,sp={borderColor:2,borderRadius:2,borderStyle:2,borderWidth:2,display:2,flex:2,inset:2,margin:2,overflow:2,overscrollBehavior:2,padding:2,insetBlock:2.1,insetInline:2.1,marginInline:2.1,marginBlock:2.1,paddingInline:2.1,paddingBlock:2.1,borderBlockStartColor:2.2,borderBlockStartStyle:2.2,borderBlockStartWidth:2.2,borderBlockEndColor:2.2,borderBlockEndStyle:2.2,borderBlockEndWidth:2.2,borderInlineStartColor:2.2,borderInlineStartStyle:2.2,borderInlineStartWidth:2.2,borderInlineEndColor:2.2,borderInlineEndStyle:2.2,borderInlineEndWidth:2.2,borderEndStartRadius:2.2,borderEndEndRadius:2.2,borderStartStartRadius:2.2,borderStartEndRadius:2.2,insetBlockEnd:2.2,insetBlockStart:2.2,insetInlineEnd:2.2,insetInlineStart:2.2,marginBlockStart:2.2,marginBlockEnd:2.2,marginInlineStart:2.2,marginInlineEnd:2.2,paddingBlockStart:2.2,paddingBlockEnd:2.2,paddingInlineStart:2.2,paddingInlineEnd:2.2},Pf="borderTopLeftRadius",Jf="borderTopRightRadius",$f="borderBottomLeftRadius",If="borderBottomRightRadius",Ff="borderLeftColor",es="borderLeftStyle",ts="borderLeftWidth",as="borderRightColor",ls="borderRightStyle",ns="borderRightWidth",rs="right",is="marginLeft",us="marginRight",cs="paddingLeft",os="paddingRight",fs="left",dc={[Pf]:Jf,[Jf]:Pf,[$f]:If,[If]:$f,[Ff]:as,[es]:ls,[ts]:ns,[as]:Ff,[ls]:es,[ns]:ts,[fs]:rs,[is]:us,[us]:is,[cs]:os,[os]:cs,[rs]:fs},Oi={borderStartStartRadius:Pf,borderStartEndRadius:Jf,borderEndStartRadius:$f,borderEndEndRadius:If,borderInlineStartColor:Ff,borderInlineStartStyle:es,borderInlineStartWidth:ts,borderInlineEndColor:as,borderInlineEndStyle:ls,borderInlineEndWidth:ns,insetInlineEnd:rs,insetInlineStart:fs,marginInlineStart:is,marginInlineEnd:us,paddingInlineStart:cs,paddingInlineEnd:os},Vm=["clear","float","textAlign"];function dp(r){var u={$$css:!0},o=[];function c(f,d,h){var b=yp(h,d),S=d+b,y=Cy.get(S),R;if(y!=null)R=y[0],o.push(y[1]);else{var x=f!==d?S:b;R=ps("r",f,x);var M=sp[f]||fp,q=mp(R,d,h),L=[q,M];o.push(L),Cy.set(S,[R,L])}return R}return Object.keys(r).sort().forEach(f=>{var d=r[f];if(d!=null){var h;if(Vm.indexOf(f)>-1){var b=c(f,f,"left"),S=c(f,f,"right");d==="start"?h=[b,S]:d==="end"&&(h=[S,b])}var y=Oi[f];if(y!=null){var R=c(f,y,d),x=c(f,dc[y],d);h=[R,x]}if(f==="transitionProperty"){for(var M=Array.isArray(d)?d:[d],q=[],L=0;L0){var A=[...M],z=[...M];q.forEach(w=>{var Y=A[w];if(typeof Y=="string"){var J=Oi[Y],j=dc[J];A[w]=J,z[w]=j;var G=c(f,f,A),ne=c(f,f,z);h=[G,ne]}})}}h==null?h=c(f,f,d):u.$$css$localize=!0,u[f]=h}}),[u,o]}function vp(r,u){var o={$$css:!0},c=[],f=r.animationKeyframes,d=Bl(r,up),h=ps("css",u,JSON.stringify(r)),b="."+h,S;if(f!=null){var y=km(f),R=y[0],x=y[1];S=R.join(","),c.push(...x)}var M=ba(Ot(Ot({},d),{},{animationName:S}));return c.push(""+b+M),o[h]=h,[o,[[c,op]]]}function hp(r,u){var o=r||cp,c={},f={},d=function(){var y=o[h],R=h,x=y;if(!Object.prototype.hasOwnProperty.call(o,h)||y==null)return"continue";Vm.indexOf(h)>-1&&(y==="start"?x=u?"right":"left":y==="end"&&(x=u?"left":"right"));var M=Oi[h];if(M!=null&&(R=u?dc[M]:M),h==="transitionProperty"){var q=Array.isArray(y)?y:[y];q.forEach((L,Q)=>{if(typeof L=="string"){var A=Oi[L];A!=null&&(q[Q]=u?dc[A]:A,x=q.join(" "))}})}c[R]||(f[R]=x),R===h&&(c[R]=!0)};for(var h in o)var b=d();return Hm(f,!0)}function yp(r,u){var o=St(r,u);return typeof o!="string"?JSON.stringify(o||""):o}function mp(r,u,o){var c=[],f="."+r;switch(u){case"animationKeyframes":{var d=km(o),h=d[0],b=d[1],S=ba({animationName:h.join(",")});c.push(""+f+S,...b);break}case"placeholderTextColor":{var y=ba({color:o,opacity:1});c.push(f+"::-webkit-input-placeholder"+y,f+"::-moz-placeholder"+y,f+":-ms-input-placeholder"+y,f+"::placeholder"+y);break}case"pointerEvents":{var R=o;if(o==="auto")R="auto!important";else if(o==="none"){R="none!important";var x=ba({pointerEvents:"none"});c.push(f+">* "+x)}else if(o==="box-none"){R="none!important";var M=ba({pointerEvents:"auto"});c.push(f+">* "+M)}else if(o==="box-only"){R="auto!important";var q=ba({pointerEvents:"none"});c.push(f+">* "+q)}var L=ba({pointerEvents:R});c.push(""+f+L);break}case"scrollbarWidth":{o==="none"&&c.push(f+"::-webkit-scrollbar{display:none}");var Q=ba({scrollbarWidth:o});c.push(""+f+Q);break}default:{var A=ba({[u]:o});c.push(""+f+A);break}}return c}function ba(r){var u=ip(Hm(r)),o=Object.keys(u).map(c=>{var f=u[c],d=Fg(c);return Array.isArray(f)?f.map(h=>d+":"+h).join(";"):d+":"+f}).sort().join(";");return"{"+o+";}"}function ps(r,u,o){var c=Pg(u+o);return r+"-"+c}function bp(r){var u=["-webkit-",""],o=ps("r","animation",JSON.stringify(r)),c="{"+Object.keys(r).map(d=>{var h=r[d],b=ba(h);return""+d+b}).join("")+"}",f=u.map(d=>"@"+d+"keyframes "+o+c);return[o,f]}function km(r){if(typeof r=="number")throw new Error("Invalid CSS keyframes type: "+typeof r);var u=[],o=[],c=Array.isArray(r)?r:[r];return c.forEach(f=>{if(typeof f=="string")u.push(f);else{var d=bp(f),h=d[0],b=d[1];u.push(h),o.push(...b)}}),[u,o]}function Of(r,u,o){if(qt){var c=u??document,f=c.getElementById(r);if(f==null)if(f=document.createElement("style"),f.setAttribute("id",r),typeof o=="string"&&f.appendChild(document.createTextNode(o)),c instanceof ShadowRoot)c.insertBefore(f,c.firstChild);else{var d=c.head;d&&d.insertBefore(f,d.firstChild)}return f.sheet}else return null}var Sp=Array.prototype.slice;function Af(r){var u={},o={};if(r!=null){var c;Sp.call(r.cssRules).forEach((h,b)=>{var S=h.cssText;if(S.indexOf("stylesheet-group")>-1)c=Rp(h),u[c]={start:b,rules:[S]};else{var y=Dy(S);y!=null&&(o[y]=!0,u[c].rules.push(S))}})}function f(h,b,S){var y=My(u),R=y.indexOf(b),x=R+1,M=y[x],q=M!=null&&u[M].start!=null?u[M].start:h.cssRules.length,L=Tp(h,S,q);if(L){u[b].start==null&&(u[b].start=q);for(var Q=x;Q{var b=u[h].rules,S=b.shift();return b.sort(),b.unshift(S),b.join(` +`)}).join(` +`)},insert(h,b){var S=Number(b);if(u[S]==null){var y=gp(S);u[S]={start:null,rules:[y]},r!=null&&f(r,S,y)}var R=Dy(h);if(R!=null&&o[R]==null&&(o[R]=!0,u[S].rules.push(h),r!=null)){var x=f(r,S,h);x||u[S].rules.pop()}}};return d}function gp(r){return'[stylesheet-group="'+r+'"]{}'}var pp=/["']/g;function Rp(r){return Number(r.selectorText.split(pp)[1])}function My(r){return Object.keys(r).map(Number).sort((u,o)=>u>o?1:-1)}var Ep=/\s*([,])\s*/g;function Dy(r){var u=r.split("{")[0].trim();return u!==""?u.replace(Ep,"$1"):null}function Tp(r,u,o){try{return r.insertRule(u,o),!0}catch{return!1}}var xp="react-native-stylesheet",_f=new WeakMap,aa=[],wy=["html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}","body{margin:0;}","button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}","input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}"];function Rs(r,u){u===void 0&&(u=xp);var o;if(qt){var c=r!=null?r.getRootNode():document;if(aa.length===0)o=Af(Of(u)),wy.forEach(b=>{o.insert(b,0)}),_f.set(c,aa.length),aa.push(o);else{var f=_f.get(c);if(f==null){var d=aa[0],h=d!=null?d.getTextContent():"";o=Af(Of(u,c,h)),_f.set(c,aa.length),aa.push(o)}else o=aa[f]}}else aa.length===0?(o=Af(Of(u)),wy.forEach(b=>{o.insert(b,0)}),aa.push(o)):o=aa[0];return{getTextContent(){return o.getTextContent()},id:u,insert(b,S){aa.forEach(y=>{y.insert(b,S)})}}}var uc={},zy;function Op(){if(zy)return uc;zy=1,Object.defineProperty(uc,"__esModule",{value:!0}),uc.localizeStyle=c;var r=new WeakMap,u="$$css$localize";function o(f,d){var h={};for(var b in f)if(b!==u){var S=f[b];Array.isArray(S)?h[b]=d?S[1]:S[0]:h[b]=S}return h}function c(f,d){if(f[u]!=null){var h=d?1:0;if(r.has(f)){var b=r.get(f),S=b[h];return S==null&&(S=o(f,d),b[h]=S,r.set(f,b)),S}var y=o(f,d),R=new Array(2);return R[h]=y,r.set(f,R),y}return f}return uc}var Cf,Ny;function Ap(){return Ny||(Ny=1,Cf=Op()),Cf}var _p=Ap(),Cp={},Qm={height:0,width:0},Mp=r=>{var u=r.shadowColor,o=r.shadowOffset,c=r.shadowOpacity,f=r.shadowRadius,d=o||Qm,h=d.height,b=d.width,S=St(b),y=St(h),R=St(f||0),x=gs(u||"black",c);if(x!=null&&S!=null&&y!=null&&R!=null)return S+" "+y+" "+R+" "+x},Dp=r=>{var u=r.textShadowColor,o=r.textShadowOffset,c=r.textShadowRadius,f=o||Qm,d=f.height,h=f.width,b=c||0,S=St(h),y=St(d),R=St(b),x=St(u,"textShadowColor");if(x&&(d!==0||h!==0||b!==0)&&S!=null&&y!=null&&R!=null)return S+" "+y+" "+R+" "+x},wp=r=>{if(typeof r=="string")return r;var u=St(r.offsetX)||0,o=St(r.offsetY)||0,c=St(r.blurRadius)||0,f=St(r.spreadDistance)||0,d=gs(r.color)||"black",h=r.inset?"inset ":"";return""+h+u+" "+o+" "+c+" "+f+" "+d},zp=r=>r.map(wp).join(", "),Np=r=>{var u=Object.keys(r)[0],o=r[u];if(u==="matrix"||u==="matrix3d")return u+"("+o.join(",")+")";var c=St(o,u);return u+"("+c+")"},Bp=r=>r.map(Np).join(" "),Hp=r=>r.map(u=>St(u)).join(" "),Up={borderBottomEndRadius:"borderEndEndRadius",borderBottomStartRadius:"borderEndStartRadius",borderTopEndRadius:"borderStartEndRadius",borderTopStartRadius:"borderStartStartRadius",borderEndColor:"borderInlineEndColor",borderEndStyle:"borderInlineEndStyle",borderEndWidth:"borderInlineEndWidth",borderStartColor:"borderInlineStartColor",borderStartStyle:"borderInlineStartStyle",borderStartWidth:"borderInlineStartWidth",end:"insetInlineEnd",marginEnd:"marginInlineEnd",marginHorizontal:"marginInline",marginStart:"marginInlineStart",marginVertical:"marginBlock",paddingEnd:"paddingInlineEnd",paddingHorizontal:"paddingInline",paddingStart:"paddingInlineStart",paddingVertical:"paddingBlock",start:"insetInlineStart"},Lp={elevation:!0,overlayColor:!0,resizeMode:!0,tintColor:!0},Km=function(u,o){o===void 0&&(o={});var c=u||Cp,f={};if(o.shadow,c.shadowColor!=null||c.shadowOffset!=null||c.shadowOpacity!=null||c.shadowRadius!=null){var d=Mp(c);d!=null&&(f.boxShadow=d)}if(o.textShadow,c.textShadowColor!=null||c.textShadowOffset!=null||c.textShadowRadius!=null){var h=Dp(c);if(h!=null&&f.textShadow==null){var b=c.textShadow,S=b?b+", "+h:h;f.textShadow=S}}for(var y in c)if(!(Lp[y]!=null||y==="shadowColor"||y==="shadowOffset"||y==="shadowOpacity"||y==="shadowRadius"||y==="textShadowColor"||y==="textShadowOffset"||y==="textShadowRadius")){var R=c[y],x=Up[y]||y,M=R;if(!(!Object.prototype.hasOwnProperty.call(c,y)||x!==y&&c[x]!=null))if(x==="aspectRatio"&&typeof M=="number")f[x]=M.toString();else if(x==="boxShadow"){Array.isArray(M)&&(M=zp(M));var q=f.boxShadow;f.boxShadow=q?M+", "+q:M}else x==="fontVariant"?(Array.isArray(M)&&M.length>0&&(M=M.join(" ")),f[x]=M):x==="textAlignVertical"?c.verticalAlign==null&&(f.verticalAlign=M==="center"?"middle":M):x==="transform"?(Array.isArray(M)&&(M=Bp(M)),f.transform=M):x==="transformOrigin"?(Array.isArray(M)&&(M=Hp(M)),f.transformOrigin=M):f[x]=M}return f},hi={},By;function qp(){if(By)return hi;By=1,Object.defineProperty(hi,"__esModule",{value:!0}),hi.styleq=void 0;var r=new WeakMap,u="$$css";function o(f){var d,h,b;return f!=null&&(d=f.disableCache===!0,h=f.disableMix===!0,b=f.transform),function(){for(var y=[],R="",x=null,M=d?null:r,q=new Array(arguments.length),L=0;L0;){var Q=q.pop();if(!(Q==null||Q===!1)){if(Array.isArray(Q)){for(var A=0;A{var o=u[0],c=u[1];vc!=null&&o.forEach(f=>{vc.insert(f,c)})})}function Xp(r){var u=dp(Km(r,Wm)),o=u[0],c=u[1];return Pm(c),o}function Vp(r,u){var o=vp(r,u),c=o[0],f=o[1];return Pm(f),c}var Jm={position:"absolute",left:0,right:0,top:0,bottom:0},kp=$m({x:Ot({},Jm)}).x;function $m(r){return Object.keys(r).forEach(u=>{var o=r[u];if(o!=null&&o.$$css!==!0){var c;u.indexOf("$raw")>-1?c=Vp(o,u.split("$raw")[0]):c=Xp(o),Zm.set(o,c)}}),r}function Qp(r,u){return[r,u]}function Kp(){for(var r=arguments.length,u=new Array(r),o=0;o{u||(u=Pp);var c=u,f=c["aria-activedescendant"],d=c.accessibilityActiveDescendant,h=c["aria-atomic"],b=c.accessibilityAtomic,S=c["aria-autocomplete"],y=c.accessibilityAutoComplete,R=c["aria-busy"],x=c.accessibilityBusy,M=c["aria-checked"],q=c.accessibilityChecked,L=c["aria-colcount"],Q=c.accessibilityColumnCount,A=c["aria-colindex"],z=c.accessibilityColumnIndex,w=c["aria-colspan"],Y=c.accessibilityColumnSpan,J=c["aria-controls"],j=c.accessibilityControls,G=c["aria-current"],ne=c.accessibilityCurrent,ie=c["aria-describedby"],me=c.accessibilityDescribedBy,be=c["aria-details"],we=c.accessibilityDetails,pe=c["aria-disabled"],Te=c.accessibilityDisabled,Qe=c["aria-errormessage"],Ae=c.accessibilityErrorMessage,D=c["aria-expanded"],X=c.accessibilityExpanded,K=c["aria-flowto"],de=c.accessibilityFlowTo,g=c["aria-haspopup"],H=c.accessibilityHasPopup,k=c["aria-hidden"],V=c.accessibilityHidden,$=c["aria-invalid"],ue=c.accessibilityInvalid,te=c["aria-keyshortcuts"],je=c.accessibilityKeyShortcuts,ce=c["aria-label"],At=c.accessibilityLabel,Za=c["aria-labelledby"],Wa=c.accessibilityLabelledBy,pa=c["aria-level"],Pa=c.accessibilityLevel,Jt=c["aria-live"],Hl=c.accessibilityLiveRegion,hn=c["aria-modal"],gt=c.accessibilityModal,Ul=c["aria-multiline"],et=c.accessibilityMultiline,ca=c["aria-multiselectable"],_t=c.accessibilityMultiSelectable,jt=c["aria-orientation"],Ja=c.accessibilityOrientation,Ll=c["aria-owns"],ql=c.accessibilityOwns,oa=c["aria-placeholder"],tt=c.accessibilityPlaceholder,Ct=c["aria-posinset"],at=c.accessibilityPosInSet,yn=c["aria-pressed"],ir=c.accessibilityPressed,mn=c["aria-readonly"],$a=c.accessibilityReadOnly,Ra=c["aria-required"],Re=c.accessibilityRequired;c.role,c.accessibilityRole;var Ea=c["aria-roledescription"],Ta=c.accessibilityRoleDescription,bn=c["aria-rowcount"],Sn=c.accessibilityRowCount,jl=c["aria-rowindex"],Yl=c.accessibilityRowIndex,ee=c["aria-rowspan"],Le=c.accessibilityRowSpan,dt=c["aria-selected"],Ia=c.accessibilitySelected,Yt=c["aria-setsize"],Fa=c.accessibilitySetSize,ur=c["aria-sort"],Sc=c.accessibilitySort,$t=c["aria-valuemax"],Je=c.accessibilityValueMax,lt=c["aria-valuemin"],el=c.accessibilityValueMin,gn=c["aria-valuenow"],gc=c.accessibilityValueNow,Hi=c["aria-valuetext"],Ui=c.accessibilityValueText,fa=c.dataSet,tl=c.focusable,xa=c.id,al=c.nativeID,ll=c.pointerEvents,nl=c.style,He=c.tabIndex,cr=c.testID,W=Bl(c,Wp),Oa=pe||Te,ut=Nm.propsToAriaRole(u),Li=f??d;Li!=null&&(W["aria-activedescendant"]=Li);var or=h!=null?f:b;or!=null&&(W["aria-atomic"]=or);var fr=S??y;fr!=null&&(W["aria-autocomplete"]=fr);var qi=R??x;qi!=null&&(W["aria-busy"]=qi);var Gl=M??q;Gl!=null&&(W["aria-checked"]=Gl);var Xl=L??Q;Xl!=null&&(W["aria-colcount"]=Xl);var Gt=A??z;Gt!=null&&(W["aria-colindex"]=Gt);var pn=w??Y;pn!=null&&(W["aria-colspan"]=pn);var sr=J??j;sr!=null&&(W["aria-controls"]=nr(sr));var Aa=G??ne;Aa!=null&&(W["aria-current"]=Aa);var Rn=ie??me;Rn!=null&&(W["aria-describedby"]=nr(Rn));var En=be??we;En!=null&&(W["aria-details"]=En),Oa===!0&&(W["aria-disabled"]=!0,(r==="button"||r==="form"||r==="input"||r==="select"||r==="textarea")&&(W.disabled=!0));var ji=Qe??Ae;ji!=null&&(W["aria-errormessage"]=ji);var dr=D??X;dr!=null&&(W["aria-expanded"]=dr);var vt=K??de;vt!=null&&(W["aria-flowto"]=nr(vt));var vr=g??H;vr!=null&&(W["aria-haspopup"]=vr);var Yi=k??V;Yi===!0&&(W["aria-hidden"]=Yi);var Vl=$??ue;Vl!=null&&(W["aria-invalid"]=Vl);var hr=te??je;hr!=null&&(W["aria-keyshortcuts"]=nr(hr));var kl=ce??At;kl!=null&&(W["aria-label"]=kl);var Gi=Za??Wa;Gi!=null&&(W["aria-labelledby"]=nr(Gi));var ht=pa??Pa;ht!=null&&(W["aria-level"]=ht);var Ql=Jt??Hl;Ql!=null&&(W["aria-live"]=Ql==="none"?"off":Ql);var yr=hn??gt;yr!=null&&(W["aria-modal"]=yr);var Tn=Ul??et;Tn!=null&&(W["aria-multiline"]=Tn);var _a=ca??_t;_a!=null&&(W["aria-multiselectable"]=_a);var mr=jt??Ja;mr!=null&&(W["aria-orientation"]=mr);var br=Ll??ql;br!=null&&(W["aria-owns"]=nr(br));var Ca=oa??tt;Ca!=null&&(W["aria-placeholder"]=Ca);var Xi=Ct??at;Xi!=null&&(W["aria-posinset"]=Xi);var Sr=yn??ir;Sr!=null&&(W["aria-pressed"]=Sr);var gr=mn??$a;gr!=null&&(W["aria-readonly"]=gr,(r==="input"||r==="select"||r==="textarea")&&(W.readOnly=!0));var xn=Ra??Re;xn!=null&&(W["aria-required"]=xn,(r==="input"||r==="select"||r==="textarea")&&(W.required=Re)),ut!=null&&(W.role=ut==="none"?"presentation":ut);var Vi=Ea??Ta;Vi!=null&&(W["aria-roledescription"]=Vi);var ki=bn??Sn;ki!=null&&(W["aria-rowcount"]=ki);var Kl=jl??Yl;Kl!=null&&(W["aria-rowindex"]=Kl);var On=ee??Le;On!=null&&(W["aria-rowspan"]=On);var An=dt??Ia;An!=null&&(W["aria-selected"]=An);var Ma=Yt??Fa;Ma!=null&&(W["aria-setsize"]=Ma);var Da=ur??Sc;Da!=null&&(W["aria-sort"]=Da);var pr=$t??Je;pr!=null&&(W["aria-valuemax"]=pr);var _n=lt??el;_n!=null&&(W["aria-valuemin"]=_n);var Rr=gn??gc;Rr!=null&&(W["aria-valuenow"]=Rr);var rl=Hi??Ui;if(rl!=null&&(W["aria-valuetext"]=rl),fa!=null){for(var Mt in fa)if(Jp.call(fa,Mt)){var Er=e1(Mt),il=fa[Mt];il!=null&&(W["data-"+Er]=il)}}He===0||He==="0"||He===-1||He==="-1"?W.tabIndex=He:(tl===!1&&(W.tabIndex="-1"),r==="a"||r==="button"||r==="input"||r==="select"||r==="textarea"?(tl===!1||Te===!0)&&(W.tabIndex="-1"):ut==="button"||ut==="checkbox"||ut==="link"||ut==="radio"||ut==="textbox"||ut==="switch"?tl!==!1&&(W.tabIndex="0"):tl===!0&&(W.tabIndex="0"));var It=ua([nl,ll&&t1[ll]],Ot({writingDirection:"ltr"},o)),Cn=It[0],Zl=It[1];Cn&&(W.className=Cn),Zl&&(W.style=Zl);var Tr=xa??al;return Tr!=null&&(W.id=Tr),cr!=null&&(W["data-testid"]=cr),W.type==null&&r==="button"&&(W.type="button"),W},Mf={exports:{}},oe={};var Hy;function l1(){if(Hy)return oe;Hy=1;var r=Symbol.for("react.transitional.element"),u=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),f=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),h=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),S=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),R=Symbol.for("react.lazy"),x=Symbol.iterator;function M(g){return g===null||typeof g!="object"?null:(g=x&&g[x]||g["@@iterator"],typeof g=="function"?g:null)}var q={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},L=Object.assign,Q={};function A(g,H,k){this.props=g,this.context=H,this.refs=Q,this.updater=k||q}A.prototype.isReactComponent={},A.prototype.setState=function(g,H){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,H,"setState")},A.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function z(){}z.prototype=A.prototype;function w(g,H,k){this.props=g,this.context=H,this.refs=Q,this.updater=k||q}var Y=w.prototype=new z;Y.constructor=w,L(Y,A.prototype),Y.isPureReactComponent=!0;var J=Array.isArray,j={H:null,A:null,T:null,S:null,V:null},G=Object.prototype.hasOwnProperty;function ne(g,H,k,V,$,ue){return k=ue.ref,{$$typeof:r,type:g,key:H,ref:k!==void 0?k:null,props:ue}}function ie(g,H){return ne(g.type,H,void 0,void 0,void 0,g.props)}function me(g){return typeof g=="object"&&g!==null&&g.$$typeof===r}function be(g){var H={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(k){return H[k]})}var we=/\/+/g;function pe(g,H){return typeof g=="object"&&g!==null&&g.key!=null?be(""+g.key):H.toString(36)}function Te(){}function Qe(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(Te,Te):(g.status="pending",g.then(function(H){g.status==="pending"&&(g.status="fulfilled",g.value=H)},function(H){g.status==="pending"&&(g.status="rejected",g.reason=H)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function Ae(g,H,k,V,$){var ue=typeof g;(ue==="undefined"||ue==="boolean")&&(g=null);var te=!1;if(g===null)te=!0;else switch(ue){case"bigint":case"string":case"number":te=!0;break;case"object":switch(g.$$typeof){case r:case u:te=!0;break;case R:return te=g._init,Ae(te(g._payload),H,k,V,$)}}if(te)return $=$(g),te=V===""?"."+pe(g,0):V,J($)?(k="",te!=null&&(k=te.replace(we,"$&/")+"/"),Ae($,H,k,"",function(At){return At})):$!=null&&(me($)&&($=ie($,k+($.key==null||g&&g.key===$.key?"":(""+$.key).replace(we,"$&/")+"/")+te)),H.push($)),1;te=0;var je=V===""?".":V+":";if(J(g))for(var ce=0;ce{var c;r&&r.constructor===String&&(c=Nm.propsToAccessibilityComponent(u));var f=c||r,d=a1(f,u,o),h=st.createElement(f,d),b=d.dir?st.createElement(u1,{children:h,direction:d.dir,locale:d.lang}):h;return b},Df={exports:{}},yi={},wf={exports:{}},zf={};var jy;function c1(){return jy||(jy=1,(function(r){function u(D,X){var K=D.length;D.push(X);e:for(;0>>1,g=D[de];if(0>>1;def(V,K))$f(ue,V)?(D[de]=ue,D[$]=K,de=$):(D[de]=V,D[k]=K,de=k);else if($f(ue,K))D[de]=ue,D[$]=K,de=$;else break e}}return X}function f(D,X){var K=D.sortIndex-X.sortIndex;return K!==0?K:D.id-X.id}if(r.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;r.unstable_now=function(){return d.now()}}else{var h=Date,b=h.now();r.unstable_now=function(){return h.now()-b}}var S=[],y=[],R=1,x=null,M=3,q=!1,L=!1,Q=!1,A=!1,z=typeof setTimeout=="function"?setTimeout:null,w=typeof clearTimeout=="function"?clearTimeout:null,Y=typeof setImmediate<"u"?setImmediate:null;function J(D){for(var X=o(y);X!==null;){if(X.callback===null)c(y);else if(X.startTime<=D)c(y),X.sortIndex=X.expirationTime,u(S,X);else break;X=o(y)}}function j(D){if(Q=!1,J(D),!L)if(o(S)!==null)L=!0,G||(G=!0,pe());else{var X=o(y);X!==null&&Ae(j,X.startTime-D)}}var G=!1,ne=-1,ie=5,me=-1;function be(){return A?!0:!(r.unstable_now()-meD&&be());){var de=x.callback;if(typeof de=="function"){x.callback=null,M=x.priorityLevel;var g=de(x.expirationTime<=D);if(D=r.unstable_now(),typeof g=="function"){x.callback=g,J(D),X=!0;break t}x===o(S)&&c(S),J(D)}else c(S);x=o(S)}if(x!==null)X=!0;else{var H=o(y);H!==null&&Ae(j,H.startTime-D),X=!1}}break e}finally{x=null,M=K,q=!1}X=void 0}}finally{X?pe():G=!1}}}var pe;if(typeof Y=="function")pe=function(){Y(we)};else if(typeof MessageChannel<"u"){var Te=new MessageChannel,Qe=Te.port2;Te.port1.onmessage=we,pe=function(){Qe.postMessage(null)}}else pe=function(){z(we,0)};function Ae(D,X){ne=z(function(){D(r.unstable_now())},X)}r.unstable_IdlePriority=5,r.unstable_ImmediatePriority=1,r.unstable_LowPriority=4,r.unstable_NormalPriority=3,r.unstable_Profiling=null,r.unstable_UserBlockingPriority=2,r.unstable_cancelCallback=function(D){D.callback=null},r.unstable_forceFrameRate=function(D){0>D||125de?(D.sortIndex=K,u(y,D),o(S)===null&&D===o(y)&&(Q?(w(ne),ne=-1):Q=!0,Ae(j,K-de))):(D.sortIndex=g,u(S,D),L||q||(L=!0,G||(G=!0,pe()))),D},r.unstable_shouldYield=be,r.unstable_wrapCallback=function(D){var X=M;return function(){var K=M;M=X;try{return D.apply(this,arguments)}finally{M=K}}}})(zf)),zf}var Yy;function o1(){return Yy||(Yy=1,wf.exports=c1()),wf.exports}var Nf={exports:{}},ot={};var Gy;function f1(){if(Gy)return ot;Gy=1;var r=Es();function u(S){var y="https://react.dev/errors/"+S;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(u){console.error(u)}}return r(),Nf.exports=f1(),Nf.exports}var Vy;function d1(){if(Vy)return yi;Vy=1;var r=o1(),u=Es(),o=s1();function c(e){var t="https://react.dev/errors/"+e;if(1g||(e.current=de[g],de[g]=null,g--)}function V(e,t){g++,de[g]=e.current,e.current=t}var $=H(null),ue=H(null),te=H(null),je=H(null);function ce(e,t){switch(V(te,t),V(ue,e),V($,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Hh(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Hh(t),e=Uh(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}k($),V($,e)}function At(){k($),k(ue),k(te)}function Za(e){e.memoizedState!==null&&V(je,e);var t=$.current,a=Uh(t,e.type);t!==a&&(V(ue,e),V($,a))}function Wa(e){ue.current===e&&(k($),k(ue)),je.current===e&&(k(je),oi._currentValue=K)}var pa=Object.prototype.hasOwnProperty,Pa=r.unstable_scheduleCallback,Jt=r.unstable_cancelCallback,Hl=r.unstable_shouldYield,hn=r.unstable_requestPaint,gt=r.unstable_now,Ul=r.unstable_getCurrentPriorityLevel,et=r.unstable_ImmediatePriority,ca=r.unstable_UserBlockingPriority,_t=r.unstable_NormalPriority,jt=r.unstable_LowPriority,Ja=r.unstable_IdlePriority,Ll=r.log,ql=r.unstable_setDisableYieldValue,oa=null,tt=null;function Ct(e){if(typeof Ll=="function"&&ql(e),tt&&typeof tt.setStrictMode=="function")try{tt.setStrictMode(oa,e)}catch{}}var at=Math.clz32?Math.clz32:mn,yn=Math.log,ir=Math.LN2;function mn(e){return e>>>=0,e===0?32:31-(yn(e)/ir|0)|0}var $a=256,Ra=4194304;function Re(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ea(e,t,a){var l=e.pendingLanes;if(l===0)return 0;var n=0,i=e.suspendedLanes,s=e.pingedLanes;e=e.warmLanes;var v=l&134217727;return v!==0?(l=v&~i,l!==0?n=Re(l):(s&=v,s!==0?n=Re(s):a||(a=v&~e,a!==0&&(n=Re(a))))):(v=l&~i,v!==0?n=Re(v):s!==0?n=Re(s):a||(a=l&~e,a!==0&&(n=Re(a)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,a=t&-t,i>=a||i===32&&(a&4194048)!==0)?t:n}function Ta(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function bn(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Sn(){var e=$a;return $a<<=1,($a&4194048)===0&&($a=256),e}function jl(){var e=Ra;return Ra<<=1,(Ra&62914560)===0&&(Ra=4194304),e}function Yl(e){for(var t=[],a=0;31>a;a++)t.push(e);return t}function ee(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Le(e,t,a,l,n,i){var s=e.pendingLanes;e.pendingLanes=a,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=a,e.entangledLanes&=a,e.errorRecoveryDisabledLanes&=a,e.shellSuspendCounter=0;var v=e.entanglements,m=e.expirationTimes,O=e.hiddenUpdates;for(a=s&~a;0)":-1n||m[l]!==O[n]){var N=` +`+m[l].replace(" at new "," at ");return e.displayName&&N.includes("")&&(N=N.replace("",e.displayName)),N}while(1<=l&&0<=n);break}}}finally{Rn=!1,Error.prepareStackTrace=a}return(a=e?e.displayName||e.name:"")?Aa(a):""}function ji(e){switch(e.tag){case 26:case 27:case 5:return Aa(e.type);case 16:return Aa("Lazy");case 13:return Aa("Suspense");case 19:return Aa("SuspenseList");case 0:case 15:return En(e.type,!1);case 11:return En(e.type.render,!1);case 1:return En(e.type,!0);case 31:return Aa("Activity");default:return""}}function dr(e){try{var t="";do t+=ji(e),e=e.return;while(e);return t}catch(a){return` +Error generating stack: `+a.message+` +`+a.stack}}function vt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function vr(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Yi(e){var t=vr(e)?"checked":"value",a=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),l=""+e[t];if(!e.hasOwnProperty(t)&&typeof a<"u"&&typeof a.get=="function"&&typeof a.set=="function"){var n=a.get,i=a.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return n.call(this)},set:function(s){l=""+s,i.call(this,s)}}),Object.defineProperty(e,t,{enumerable:a.enumerable}),{getValue:function(){return l},setValue:function(s){l=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Vl(e){e._valueTracker||(e._valueTracker=Yi(e))}function hr(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var a=t.getValue(),l="";return e&&(l=vr(e)?e.checked?"true":"false":e.value),e=l,e!==a?(t.setValue(e),!0):!1}function kl(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Gi=/[\n"\\]/g;function ht(e){return e.replace(Gi,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Ql(e,t,a,l,n,i,s,v){e.name="",s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.type=s:e.removeAttribute("type"),t!=null?s==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+vt(t)):e.value!==""+vt(t)&&(e.value=""+vt(t)):s!=="submit"&&s!=="reset"||e.removeAttribute("value"),t!=null?Tn(e,s,vt(t)):a!=null?Tn(e,s,vt(a)):l!=null&&e.removeAttribute("value"),n==null&&i!=null&&(e.defaultChecked=!!i),n!=null&&(e.checked=n&&typeof n!="function"&&typeof n!="symbol"),v!=null&&typeof v!="function"&&typeof v!="symbol"&&typeof v!="boolean"?e.name=""+vt(v):e.removeAttribute("name")}function yr(e,t,a,l,n,i,s,v){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||a!=null){if(!(i!=="submit"&&i!=="reset"||t!=null))return;a=a!=null?""+vt(a):"",t=t!=null?""+vt(t):a,v||t===e.value||(e.value=t),e.defaultValue=t}l=l??n,l=typeof l!="function"&&typeof l!="symbol"&&!!l,e.checked=v?e.checked:!!l,e.defaultChecked=!!l,s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(e.name=s)}function Tn(e,t,a){t==="number"&&kl(e.ownerDocument)===e||e.defaultValue===""+a||(e.defaultValue=""+a)}function _a(e,t,a,l){if(e=e.options,t){t={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Er=!1;if(Mt)try{var il={};Object.defineProperty(il,"passive",{get:function(){Er=!0}}),window.addEventListener("test",il,il),window.removeEventListener("test",il,il)}catch{Er=!1}var It=null,Cn=null,Zl=null;function Tr(){if(Zl)return Zl;var e,t=Cn,a=t.length,l,n="value"in It?It.value:It.textContent,i=n.length;for(e=0;e=Ar),Qs=" ",Ks=!1;function Zs(e,t){switch(e){case"keyup":return Ib.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Ws(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Mn=!1;function eS(e,t){switch(e){case"compositionend":return Ws(t);case"keypress":return t.which!==32?null:(Ks=!0,Qs);case"textInput":return e=t.data,e===Qs&&Ks?null:e;default:return null}}function tS(e,t){if(Mn)return e==="compositionend"||!xc&&Zs(e,t)?(e=Tr(),Zl=Cn=It=null,Mn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:a,offset:t-e};e=l}e:{for(;a;){if(a.nextSibling){a=a.nextSibling;break e}a=a.parentNode}a=void 0}a=ad(a)}}function nd(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?nd(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function rd(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=kl(e.document);t instanceof e.HTMLIFrameElement;){try{var a=typeof t.contentWindow.location.href=="string"}catch{a=!1}if(a)e=t.contentWindow;else break;t=kl(e.document)}return t}function _c(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var oS=Mt&&"documentMode"in document&&11>=document.documentMode,Dn=null,Cc=null,Dr=null,Mc=!1;function id(e,t,a){var l=a.window===a?a.document:a.nodeType===9?a:a.ownerDocument;Mc||Dn==null||Dn!==kl(l)||(l=Dn,"selectionStart"in l&&_c(l)?l={start:l.selectionStart,end:l.selectionEnd}:(l=(l.ownerDocument&&l.ownerDocument.defaultView||window).getSelection(),l={anchorNode:l.anchorNode,anchorOffset:l.anchorOffset,focusNode:l.focusNode,focusOffset:l.focusOffset}),Dr&&Mr(Dr,l)||(Dr=l,l=Bu(Cc,"onSelect"),0>=s,n-=s,za=1<<32-at(t)+n|a<i?i:8;var s=D.T,v={};D.T=v,yo(e,!1,t,a);try{var m=n(),O=D.S;if(O!==null&&O(v,m),m!==null&&typeof m=="object"&&typeof m.then=="function"){var N=SS(m,l);Qr(e,t,N,Ht(e))}else Qr(e,t,l,Ht(e))}catch(U){Qr(e,t,{then:function(){},status:"rejected",reason:U},Ht())}finally{X.p=i,D.T=s}}function TS(){}function vo(e,t,a,l){if(e.tag!==5)throw Error(c(476));var n=uv(e).queue;iv(e,n,t,K,a===null?TS:function(){return cv(e),a(l)})}function uv(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:K,baseState:K,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:K},next:null};var a={};return t.next={memoizedState:a,baseState:a,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:a},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function cv(e){var t=uv(e).next.queue;Qr(e,t,{},Ht())}function ho(){return ct(oi)}function ov(){return Ze().memoizedState}function fv(){return Ze().memoizedState}function xS(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var a=Ht();e=ol(a);var l=fl(t,e,a);l!==null&&(Ut(l,t,a),jr(l,t,a)),t={cache:Vc()},e.payload=t;return}t=t.return}}function OS(e,t,a){var l=Ht();a={lane:l,revertLane:0,action:a,hasEagerState:!1,eagerState:null,next:null},hu(e)?dv(t,a):(a=Nc(e,t,a,l),a!==null&&(Ut(a,e,l),vv(a,t,l)))}function sv(e,t,a){var l=Ht();Qr(e,t,a,l)}function Qr(e,t,a,l){var n={lane:l,revertLane:0,action:a,hasEagerState:!1,eagerState:null,next:null};if(hu(e))dv(t,n);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,v=i(s,a);if(n.hasEagerState=!0,n.eagerState=v,Dt(v,s))return $i(e,t,n,0),Be===null&&Ji(),!1}catch{}if(a=Nc(e,t,n,l),a!==null)return Ut(a,e,l),vv(a,t,l),!0}return!1}function yo(e,t,a,l){if(l={lane:2,revertLane:Zo(),action:l,hasEagerState:!1,eagerState:null,next:null},hu(e)){if(t)throw Error(c(479))}else t=Nc(e,a,l,2),t!==null&&Ut(t,e,2)}function hu(e){var t=e.alternate;return e===se||t!==null&&t===se}function dv(e,t){Yn=cu=!0;var a=e.pending;a===null?t.next=t:(t.next=a.next,a.next=t),e.pending=t}function vv(e,t,a){if((a&4194048)!==0){var l=t.lanes;l&=e.pendingLanes,a|=l,t.lanes=a,Ia(e,a)}}var yu={readContext:ct,use:fu,useCallback:Ve,useContext:Ve,useEffect:Ve,useImperativeHandle:Ve,useLayoutEffect:Ve,useInsertionEffect:Ve,useMemo:Ve,useReducer:Ve,useRef:Ve,useState:Ve,useDebugValue:Ve,useDeferredValue:Ve,useTransition:Ve,useSyncExternalStore:Ve,useId:Ve,useHostTransitionStatus:Ve,useFormState:Ve,useActionState:Ve,useOptimistic:Ve,useMemoCache:Ve,useCacheRefresh:Ve},hv={readContext:ct,use:fu,useCallback:function(e,t){return Rt().memoizedState=[e,t===void 0?null:t],e},useContext:ct,useEffect:$d,useImperativeHandle:function(e,t,a){a=a!=null?a.concat([e]):null,vu(4194308,4,tv.bind(null,t,e),a)},useLayoutEffect:function(e,t){return vu(4194308,4,e,t)},useInsertionEffect:function(e,t){vu(4,2,e,t)},useMemo:function(e,t){var a=Rt();t=t===void 0?null:t;var l=e();if(rn){Ct(!0);try{e()}finally{Ct(!1)}}return a.memoizedState=[l,t],l},useReducer:function(e,t,a){var l=Rt();if(a!==void 0){var n=a(t);if(rn){Ct(!0);try{a(t)}finally{Ct(!1)}}}else n=t;return l.memoizedState=l.baseState=n,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},l.queue=e,e=e.dispatch=OS.bind(null,se,e),[l.memoizedState,e]},useRef:function(e){var t=Rt();return e={current:e},t.memoizedState=e},useState:function(e){e=co(e);var t=e.queue,a=sv.bind(null,se,t);return t.dispatch=a,[e.memoizedState,a]},useDebugValue:fo,useDeferredValue:function(e,t){var a=Rt();return so(a,e,t)},useTransition:function(){var e=co(!1);return e=iv.bind(null,se,e.queue,!0,!1),Rt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,a){var l=se,n=Rt();if(Ee){if(a===void 0)throw Error(c(407));a=a()}else{if(a=t(),Be===null)throw Error(c(349));(Se&124)!==0||Hd(l,t,a)}n.memoizedState=a;var i={value:a,getSnapshot:t};return n.queue=i,$d(Ld.bind(null,l,i,e),[e]),l.flags|=2048,Xn(9,du(),Ud.bind(null,l,i,a,t),null),a},useId:function(){var e=Rt(),t=Be.identifierPrefix;if(Ee){var a=Na,l=za;a=(l&~(1<<32-at(l)-1)).toString(32)+a,t="«"+t+"R"+a,a=ou++,0le?(Fe=I,I=null):Fe=I.sibling;var ge=_(E,I,T[le],B);if(ge===null){I===null&&(I=Fe);break}e&&I&&ge.alternate===null&&t(E,I),p=i(ge,p,le),ve===null?P=ge:ve.sibling=ge,ve=ge,I=Fe}if(le===T.length)return a(E,I),Ee&&Fl(E,le),P;if(I===null){for(;lele?(Fe=I,I=null):Fe=I.sibling;var _l=_(E,I,ge.value,B);if(_l===null){I===null&&(I=Fe);break}e&&I&&_l.alternate===null&&t(E,I),p=i(_l,p,le),ve===null?P=_l:ve.sibling=_l,ve=_l,I=Fe}if(ge.done)return a(E,I),Ee&&Fl(E,le),P;if(I===null){for(;!ge.done;le++,ge=T.next())ge=U(E,ge.value,B),ge!==null&&(p=i(ge,p,le),ve===null?P=ge:ve.sibling=ge,ve=ge);return Ee&&Fl(E,le),P}for(I=l(I);!ge.done;le++,ge=T.next())ge=C(I,E,le,ge.value,B),ge!==null&&(e&&ge.alternate!==null&&I.delete(ge.key===null?le:ge.key),p=i(ge,p,le),ve===null?P=ge:ve.sibling=ge,ve=ge);return e&&I.forEach(function(_g){return t(E,_g)}),Ee&&Fl(E,le),P}function Me(E,p,T,B){if(typeof T=="object"&&T!==null&&T.type===L&&T.key===null&&(T=T.props.children),typeof T=="object"&&T!==null){switch(T.$$typeof){case M:e:{for(var P=T.key;p!==null;){if(p.key===P){if(P=T.type,P===L){if(p.tag===7){a(E,p.sibling),B=n(p,T.props.children),B.return=E,E=B;break e}}else if(p.elementType===P||typeof P=="object"&&P!==null&&P.$$typeof===ie&&mv(P)===p.type){a(E,p.sibling),B=n(p,T.props),Zr(B,T),B.return=E,E=B;break e}a(E,p);break}else t(E,p);p=p.sibling}T.type===L?(B=$l(T.props.children,E.mode,B,T.key),B.return=E,E=B):(B=Fi(T.type,T.key,T.props,null,E.mode,B),Zr(B,T),B.return=E,E=B)}return s(E);case q:e:{for(P=T.key;p!==null;){if(p.key===P)if(p.tag===4&&p.stateNode.containerInfo===T.containerInfo&&p.stateNode.implementation===T.implementation){a(E,p.sibling),B=n(p,T.children||[]),B.return=E,E=B;break e}else{a(E,p);break}else t(E,p);p=p.sibling}B=Uc(T,E.mode,B),B.return=E,E=B}return s(E);case ie:return P=T._init,T=P(T._payload),Me(E,p,T,B)}if(Ae(T))return re(E,p,T,B);if(pe(T)){if(P=pe(T),typeof P!="function")throw Error(c(150));return T=P.call(T),ae(E,p,T,B)}if(typeof T.then=="function")return Me(E,p,mu(T),B);if(T.$$typeof===Y)return Me(E,p,lu(E,T),B);bu(E,T)}return typeof T=="string"&&T!==""||typeof T=="number"||typeof T=="bigint"?(T=""+T,p!==null&&p.tag===6?(a(E,p.sibling),B=n(p,T),B.return=E,E=B):(a(E,p),B=Hc(T,E.mode,B),B.return=E,E=B),s(E)):a(E,p)}return function(E,p,T,B){try{Kr=0;var P=Me(E,p,T,B);return Vn=null,P}catch(I){if(I===Lr||I===ru)throw I;var ve=wt(29,I,null,E.mode);return ve.lanes=B,ve.return=E,ve}}}var kn=bv(!0),Sv=bv(!1),Kt=H(null),da=null;function dl(e){var t=e.alternate;V(Pe,Pe.current&1),V(Kt,e),da===null&&(t===null||jn.current!==null||t.memoizedState!==null)&&(da=e)}function gv(e){if(e.tag===22){if(V(Pe,Pe.current),V(Kt,e),da===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(da=e)}}else vl()}function vl(){V(Pe,Pe.current),V(Kt,Kt.current)}function La(e){k(Kt),da===e&&(da=null),k(Pe)}var Pe=H(0);function Su(e){for(var t=e;t!==null;){if(t.tag===13){var a=t.memoizedState;if(a!==null&&(a=a.dehydrated,a===null||a.data==="$?"||rf(a)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function mo(e,t,a,l){t=e.memoizedState,a=a(l,t),a=a==null?t:R({},t,a),e.memoizedState=a,e.lanes===0&&(e.updateQueue.baseState=a)}var bo={enqueueSetState:function(e,t,a){e=e._reactInternals;var l=Ht(),n=ol(l);n.payload=t,a!=null&&(n.callback=a),t=fl(e,n,l),t!==null&&(Ut(t,e,l),jr(t,e,l))},enqueueReplaceState:function(e,t,a){e=e._reactInternals;var l=Ht(),n=ol(l);n.tag=1,n.payload=t,a!=null&&(n.callback=a),t=fl(e,n,l),t!==null&&(Ut(t,e,l),jr(t,e,l))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var a=Ht(),l=ol(a);l.tag=2,t!=null&&(l.callback=t),t=fl(e,l,a),t!==null&&(Ut(t,e,a),jr(t,e,a))}};function pv(e,t,a,l,n,i,s){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(l,i,s):t.prototype&&t.prototype.isPureReactComponent?!Mr(a,l)||!Mr(n,i):!0}function Rv(e,t,a,l){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(a,l),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(a,l),t.state!==e&&bo.enqueueReplaceState(t,t.state,null)}function un(e,t){var a=t;if("ref"in t){a={};for(var l in t)l!=="ref"&&(a[l]=t[l])}if(e=e.defaultProps){a===t&&(a=R({},a));for(var n in e)a[n]===void 0&&(a[n]=e[n])}return a}var gu=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Ev(e){gu(e)}function Tv(e){console.error(e)}function xv(e){gu(e)}function pu(e,t){try{var a=e.onUncaughtError;a(t.value,{componentStack:t.stack})}catch(l){setTimeout(function(){throw l})}}function Ov(e,t,a){try{var l=e.onCaughtError;l(a.value,{componentStack:a.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(n){setTimeout(function(){throw n})}}function So(e,t,a){return a=ol(a),a.tag=3,a.payload={element:null},a.callback=function(){pu(e,t)},a}function Av(e){return e=ol(e),e.tag=3,e}function _v(e,t,a,l){var n=a.type.getDerivedStateFromError;if(typeof n=="function"){var i=l.value;e.payload=function(){return n(i)},e.callback=function(){Ov(t,a,l)}}var s=a.stateNode;s!==null&&typeof s.componentDidCatch=="function"&&(e.callback=function(){Ov(t,a,l),typeof n!="function"&&(gl===null?gl=new Set([this]):gl.add(this));var v=l.stack;this.componentDidCatch(l.value,{componentStack:v!==null?v:""})})}function _S(e,t,a,l,n){if(a.flags|=32768,l!==null&&typeof l=="object"&&typeof l.then=="function"){if(t=a.alternate,t!==null&&Br(t,a,n,!0),a=Kt.current,a!==null){switch(a.tag){case 13:return da===null?Xo():a.alternate===null&&Ge===0&&(Ge=3),a.flags&=-257,a.flags|=65536,a.lanes=n,l===Kc?a.flags|=16384:(t=a.updateQueue,t===null?a.updateQueue=new Set([l]):t.add(l),ko(e,l,n)),!1;case 22:return a.flags|=65536,l===Kc?a.flags|=16384:(t=a.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([l])},a.updateQueue=t):(a=t.retryQueue,a===null?t.retryQueue=new Set([l]):a.add(l)),ko(e,l,n)),!1}throw Error(c(435,a.tag))}return ko(e,l,n),Xo(),!1}if(Ee)return t=Kt.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=n,l!==jc&&(e=Error(c(422),{cause:l}),Nr(Xt(e,a)))):(l!==jc&&(t=Error(c(423),{cause:l}),Nr(Xt(t,a))),e=e.current.alternate,e.flags|=65536,n&=-n,e.lanes|=n,l=Xt(l,a),n=So(e.stateNode,l,n),Pc(e,n),Ge!==4&&(Ge=2)),!1;var i=Error(c(520),{cause:l});if(i=Xt(i,a),ei===null?ei=[i]:ei.push(i),Ge!==4&&(Ge=2),t===null)return!0;l=Xt(l,a),a=t;do{switch(a.tag){case 3:return a.flags|=65536,e=n&-n,a.lanes|=e,e=So(a.stateNode,l,e),Pc(a,e),!1;case 1:if(t=a.type,i=a.stateNode,(a.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||i!==null&&typeof i.componentDidCatch=="function"&&(gl===null||!gl.has(i))))return a.flags|=65536,n&=-n,a.lanes|=n,n=Av(n),_v(n,e,a,l),Pc(a,n),!1}a=a.return}while(a!==null);return!1}var Cv=Error(c(461)),$e=!1;function nt(e,t,a,l){t.child=e===null?Sv(t,null,a,l):kn(t,e.child,a,l)}function Mv(e,t,a,l,n){a=a.render;var i=t.ref;if("ref"in l){var s={};for(var v in l)v!=="ref"&&(s[v]=l[v])}else s=l;return ln(t),l=eo(e,t,a,s,i,n),v=to(),e!==null&&!$e?(ao(e,t,n),qa(e,t,n)):(Ee&&v&&Lc(t),t.flags|=1,nt(e,t,l,n),t.child)}function Dv(e,t,a,l,n){if(e===null){var i=a.type;return typeof i=="function"&&!Bc(i)&&i.defaultProps===void 0&&a.compare===null?(t.tag=15,t.type=i,wv(e,t,i,l,n)):(e=Fi(a.type,null,l,t,t.mode,n),e.ref=t.ref,e.return=t,t.child=e)}if(i=e.child,!Ao(e,n)){var s=i.memoizedProps;if(a=a.compare,a=a!==null?a:Mr,a(s,l)&&e.ref===t.ref)return qa(e,t,n)}return t.flags|=1,e=wa(i,l),e.ref=t.ref,e.return=t,t.child=e}function wv(e,t,a,l,n){if(e!==null){var i=e.memoizedProps;if(Mr(i,l)&&e.ref===t.ref)if($e=!1,t.pendingProps=l=i,Ao(e,n))(e.flags&131072)!==0&&($e=!0);else return t.lanes=e.lanes,qa(e,t,n)}return go(e,t,a,l,n)}function zv(e,t,a){var l=t.pendingProps,n=l.children,i=e!==null?e.memoizedState:null;if(l.mode==="hidden"){if((t.flags&128)!==0){if(l=i!==null?i.baseLanes|a:a,e!==null){for(n=t.child=e.child,i=0;n!==null;)i=i|n.lanes|n.childLanes,n=n.sibling;t.childLanes=i&~l}else t.childLanes=0,t.child=null;return Nv(e,t,l,a)}if((a&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&nu(t,i!==null?i.cachePool:null),i!==null?wd(t,i):$c(),gv(t);else return t.lanes=t.childLanes=536870912,Nv(e,t,i!==null?i.baseLanes|a:a,a)}else i!==null?(nu(t,i.cachePool),wd(t,i),vl(),t.memoizedState=null):(e!==null&&nu(t,null),$c(),vl());return nt(e,t,n,a),t.child}function Nv(e,t,a,l){var n=Qc();return n=n===null?null:{parent:We._currentValue,pool:n},t.memoizedState={baseLanes:a,cachePool:n},e!==null&&nu(t,null),$c(),gv(t),e!==null&&Br(e,t,l,!0),null}function Ru(e,t){var a=t.ref;if(a===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof a!="function"&&typeof a!="object")throw Error(c(284));(e===null||e.ref!==a)&&(t.flags|=4194816)}}function go(e,t,a,l,n){return ln(t),a=eo(e,t,a,l,void 0,n),l=to(),e!==null&&!$e?(ao(e,t,n),qa(e,t,n)):(Ee&&l&&Lc(t),t.flags|=1,nt(e,t,a,n),t.child)}function Bv(e,t,a,l,n,i){return ln(t),t.updateQueue=null,a=Nd(t,l,a,n),zd(e),l=to(),e!==null&&!$e?(ao(e,t,i),qa(e,t,i)):(Ee&&l&&Lc(t),t.flags|=1,nt(e,t,a,i),t.child)}function Hv(e,t,a,l,n){if(ln(t),t.stateNode===null){var i=Bn,s=a.contextType;typeof s=="object"&&s!==null&&(i=ct(s)),i=new a(l,i),t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,i.updater=bo,t.stateNode=i,i._reactInternals=t,i=t.stateNode,i.props=l,i.state=t.memoizedState,i.refs={},Zc(t),s=a.contextType,i.context=typeof s=="object"&&s!==null?ct(s):Bn,i.state=t.memoizedState,s=a.getDerivedStateFromProps,typeof s=="function"&&(mo(t,a,s,l),i.state=t.memoizedState),typeof a.getDerivedStateFromProps=="function"||typeof i.getSnapshotBeforeUpdate=="function"||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(s=i.state,typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount(),s!==i.state&&bo.enqueueReplaceState(i,i.state,null),Gr(t,l,i,n),Yr(),i.state=t.memoizedState),typeof i.componentDidMount=="function"&&(t.flags|=4194308),l=!0}else if(e===null){i=t.stateNode;var v=t.memoizedProps,m=un(a,v);i.props=m;var O=i.context,N=a.contextType;s=Bn,typeof N=="object"&&N!==null&&(s=ct(N));var U=a.getDerivedStateFromProps;N=typeof U=="function"||typeof i.getSnapshotBeforeUpdate=="function",v=t.pendingProps!==v,N||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(v||O!==s)&&Rv(t,i,l,s),cl=!1;var _=t.memoizedState;i.state=_,Gr(t,l,i,n),Yr(),O=t.memoizedState,v||_!==O||cl?(typeof U=="function"&&(mo(t,a,U,l),O=t.memoizedState),(m=cl||pv(t,a,m,l,_,O,s))?(N||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=l,t.memoizedState=O),i.props=l,i.state=O,i.context=s,l=m):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),l=!1)}else{i=t.stateNode,Wc(e,t),s=t.memoizedProps,N=un(a,s),i.props=N,U=t.pendingProps,_=i.context,O=a.contextType,m=Bn,typeof O=="object"&&O!==null&&(m=ct(O)),v=a.getDerivedStateFromProps,(O=typeof v=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s!==U||_!==m)&&Rv(t,i,l,m),cl=!1,_=t.memoizedState,i.state=_,Gr(t,l,i,n),Yr();var C=t.memoizedState;s!==U||_!==C||cl||e!==null&&e.dependencies!==null&&au(e.dependencies)?(typeof v=="function"&&(mo(t,a,v,l),C=t.memoizedState),(N=cl||pv(t,a,N,l,_,C,m)||e!==null&&e.dependencies!==null&&au(e.dependencies))?(O||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(l,C,m),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(l,C,m)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=1024),t.memoizedProps=l,t.memoizedState=C),i.props=l,i.state=C,i.context=m,l=N):(typeof i.componentDidUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=1024),l=!1)}return i=l,Ru(e,t),l=(t.flags&128)!==0,i||l?(i=t.stateNode,a=l&&typeof a.getDerivedStateFromError!="function"?null:i.render(),t.flags|=1,e!==null&&l?(t.child=kn(t,e.child,null,n),t.child=kn(t,null,a,n)):nt(e,t,a,n),t.memoizedState=i.state,e=t.child):e=qa(e,t,n),e}function Uv(e,t,a,l){return zr(),t.flags|=256,nt(e,t,a,l),t.child}var po={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ro(e){return{baseLanes:e,cachePool:Td()}}function Eo(e,t,a){return e=e!==null?e.childLanes&~a:0,t&&(e|=Zt),e}function Lv(e,t,a){var l=t.pendingProps,n=!1,i=(t.flags&128)!==0,s;if((s=i)||(s=e!==null&&e.memoizedState===null?!1:(Pe.current&2)!==0),s&&(n=!0,t.flags&=-129),s=(t.flags&32)!==0,t.flags&=-33,e===null){if(Ee){if(n?dl(t):vl(),Ee){var v=Ye,m;if(m=v){e:{for(m=v,v=sa;m.nodeType!==8;){if(!v){v=null;break e}if(m=ta(m.nextSibling),m===null){v=null;break e}}v=m}v!==null?(t.memoizedState={dehydrated:v,treeContext:Il!==null?{id:za,overflow:Na}:null,retryLane:536870912,hydrationErrors:null},m=wt(18,null,null,0),m.stateNode=v,m.return=t,t.child=m,yt=t,Ye=null,m=!0):m=!1}m||tn(t)}if(v=t.memoizedState,v!==null&&(v=v.dehydrated,v!==null))return rf(v)?t.lanes=32:t.lanes=536870912,null;La(t)}return v=l.children,l=l.fallback,n?(vl(),n=t.mode,v=Eu({mode:"hidden",children:v},n),l=$l(l,n,a,null),v.return=t,l.return=t,v.sibling=l,t.child=v,n=t.child,n.memoizedState=Ro(a),n.childLanes=Eo(e,s,a),t.memoizedState=po,l):(dl(t),To(t,v))}if(m=e.memoizedState,m!==null&&(v=m.dehydrated,v!==null)){if(i)t.flags&256?(dl(t),t.flags&=-257,t=xo(e,t,a)):t.memoizedState!==null?(vl(),t.child=e.child,t.flags|=128,t=null):(vl(),n=l.fallback,v=t.mode,l=Eu({mode:"visible",children:l.children},v),n=$l(n,v,a,null),n.flags|=2,l.return=t,n.return=t,l.sibling=n,t.child=l,kn(t,e.child,null,a),l=t.child,l.memoizedState=Ro(a),l.childLanes=Eo(e,s,a),t.memoizedState=po,t=n);else if(dl(t),rf(v)){if(s=v.nextSibling&&v.nextSibling.dataset,s)var O=s.dgst;s=O,l=Error(c(419)),l.stack="",l.digest=s,Nr({value:l,source:null,stack:null}),t=xo(e,t,a)}else if($e||Br(e,t,a,!1),s=(a&e.childLanes)!==0,$e||s){if(s=Be,s!==null&&(l=a&-a,l=(l&42)!==0?1:Yt(l),l=(l&(s.suspendedLanes|a))!==0?0:l,l!==0&&l!==m.retryLane))throw m.retryLane=l,Nn(e,l),Ut(s,e,l),Cv;v.data==="$?"||Xo(),t=xo(e,t,a)}else v.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=m.treeContext,Ye=ta(v.nextSibling),yt=t,Ee=!0,en=null,sa=!1,e!==null&&(kt[Qt++]=za,kt[Qt++]=Na,kt[Qt++]=Il,za=e.id,Na=e.overflow,Il=t),t=To(t,l.children),t.flags|=4096);return t}return n?(vl(),n=l.fallback,v=t.mode,m=e.child,O=m.sibling,l=wa(m,{mode:"hidden",children:l.children}),l.subtreeFlags=m.subtreeFlags&65011712,O!==null?n=wa(O,n):(n=$l(n,v,a,null),n.flags|=2),n.return=t,l.return=t,l.sibling=n,t.child=l,l=n,n=t.child,v=e.child.memoizedState,v===null?v=Ro(a):(m=v.cachePool,m!==null?(O=We._currentValue,m=m.parent!==O?{parent:O,pool:O}:m):m=Td(),v={baseLanes:v.baseLanes|a,cachePool:m}),n.memoizedState=v,n.childLanes=Eo(e,s,a),t.memoizedState=po,l):(dl(t),a=e.child,e=a.sibling,a=wa(a,{mode:"visible",children:l.children}),a.return=t,a.sibling=null,e!==null&&(s=t.deletions,s===null?(t.deletions=[e],t.flags|=16):s.push(e)),t.child=a,t.memoizedState=null,a)}function To(e,t){return t=Eu({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Eu(e,t){return e=wt(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function xo(e,t,a){return kn(t,e.child,null,a),e=To(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function qv(e,t,a){e.lanes|=t;var l=e.alternate;l!==null&&(l.lanes|=t),Gc(e.return,t,a)}function Oo(e,t,a,l,n){var i=e.memoizedState;i===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:l,tail:a,tailMode:n}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=l,i.tail=a,i.tailMode=n)}function jv(e,t,a){var l=t.pendingProps,n=l.revealOrder,i=l.tail;if(nt(e,t,l.children,a),l=Pe.current,(l&2)!==0)l=l&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&qv(e,a,t);else if(e.tag===19)qv(e,a,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}l&=1}switch(V(Pe,l),n){case"forwards":for(a=t.child,n=null;a!==null;)e=a.alternate,e!==null&&Su(e)===null&&(n=a),a=a.sibling;a=n,a===null?(n=t.child,t.child=null):(n=a.sibling,a.sibling=null),Oo(t,!1,n,a,i);break;case"backwards":for(a=null,n=t.child,t.child=null;n!==null;){if(e=n.alternate,e!==null&&Su(e)===null){t.child=n;break}e=n.sibling,n.sibling=a,a=n,n=e}Oo(t,!0,a,null,i);break;case"together":Oo(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function qa(e,t,a){if(e!==null&&(t.dependencies=e.dependencies),Sl|=t.lanes,(a&t.childLanes)===0)if(e!==null){if(Br(e,t,a,!1),(a&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(c(153));if(t.child!==null){for(e=t.child,a=wa(e,e.pendingProps),t.child=a,a.return=t;e.sibling!==null;)e=e.sibling,a=a.sibling=wa(e,e.pendingProps),a.return=t;a.sibling=null}return t.child}function Ao(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&au(e)))}function CS(e,t,a){switch(t.tag){case 3:ce(t,t.stateNode.containerInfo),ul(t,We,e.memoizedState.cache),zr();break;case 27:case 5:Za(t);break;case 4:ce(t,t.stateNode.containerInfo);break;case 10:ul(t,t.type,t.memoizedProps.value);break;case 13:var l=t.memoizedState;if(l!==null)return l.dehydrated!==null?(dl(t),t.flags|=128,null):(a&t.child.childLanes)!==0?Lv(e,t,a):(dl(t),e=qa(e,t,a),e!==null?e.sibling:null);dl(t);break;case 19:var n=(e.flags&128)!==0;if(l=(a&t.childLanes)!==0,l||(Br(e,t,a,!1),l=(a&t.childLanes)!==0),n){if(l)return jv(e,t,a);t.flags|=128}if(n=t.memoizedState,n!==null&&(n.rendering=null,n.tail=null,n.lastEffect=null),V(Pe,Pe.current),l)break;return null;case 22:case 23:return t.lanes=0,zv(e,t,a);case 24:ul(t,We,e.memoizedState.cache)}return qa(e,t,a)}function Yv(e,t,a){if(e!==null)if(e.memoizedProps!==t.pendingProps)$e=!0;else{if(!Ao(e,a)&&(t.flags&128)===0)return $e=!1,CS(e,t,a);$e=(e.flags&131072)!==0}else $e=!1,Ee&&(t.flags&1048576)!==0&&md(t,tu,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var l=t.elementType,n=l._init;if(l=n(l._payload),t.type=l,typeof l=="function")Bc(l)?(e=un(l,e),t.tag=1,t=Hv(null,t,l,e,a)):(t.tag=0,t=go(null,t,l,e,a));else{if(l!=null){if(n=l.$$typeof,n===J){t.tag=11,t=Mv(null,t,l,e,a);break e}else if(n===ne){t.tag=14,t=Dv(null,t,l,e,a);break e}}throw t=Qe(l)||l,Error(c(306,t,""))}}return t;case 0:return go(e,t,t.type,t.pendingProps,a);case 1:return l=t.type,n=un(l,t.pendingProps),Hv(e,t,l,n,a);case 3:e:{if(ce(t,t.stateNode.containerInfo),e===null)throw Error(c(387));l=t.pendingProps;var i=t.memoizedState;n=i.element,Wc(e,t),Gr(t,l,null,a);var s=t.memoizedState;if(l=s.cache,ul(t,We,l),l!==i.cache&&Xc(t,[We],a,!0),Yr(),l=s.element,i.isDehydrated)if(i={element:l,isDehydrated:!1,cache:s.cache},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){t=Uv(e,t,l,a);break e}else if(l!==n){n=Xt(Error(c(424)),t),Nr(n),t=Uv(e,t,l,a);break e}else for(e=t.stateNode.containerInfo,e.nodeType===9?e=e.body:e=e.nodeName==="HTML"?e.ownerDocument.body:e,Ye=ta(e.firstChild),yt=t,Ee=!0,en=null,sa=!0,a=Sv(t,null,l,a),t.child=a;a;)a.flags=a.flags&-3|4096,a=a.sibling;else{if(zr(),l===n){t=qa(e,t,a);break e}nt(e,t,l,a)}t=t.child}return t;case 26:return Ru(e,t),e===null?(a=kh(t.type,null,t.pendingProps,null))?t.memoizedState=a:Ee||(a=t.type,e=t.pendingProps,l=Uu(te.current).createElement(a),l[Je]=t,l[lt]=e,it(l,a,e),He(l),t.stateNode=l):t.memoizedState=kh(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return Za(t),e===null&&Ee&&(l=t.stateNode=Gh(t.type,t.pendingProps,te.current),yt=t,sa=!0,n=Ye,El(t.type)?(uf=n,Ye=ta(l.firstChild)):Ye=n),nt(e,t,t.pendingProps.children,a),Ru(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&Ee&&((n=l=Ye)&&(l=ag(l,t.type,t.pendingProps,sa),l!==null?(t.stateNode=l,yt=t,Ye=ta(l.firstChild),sa=!1,n=!0):n=!1),n||tn(t)),Za(t),n=t.type,i=t.pendingProps,s=e!==null?e.memoizedProps:null,l=i.children,af(n,i)?l=null:s!==null&&af(n,s)&&(t.flags|=32),t.memoizedState!==null&&(n=eo(e,t,pS,null,null,a),oi._currentValue=n),Ru(e,t),nt(e,t,l,a),t.child;case 6:return e===null&&Ee&&((e=a=Ye)&&(a=lg(a,t.pendingProps,sa),a!==null?(t.stateNode=a,yt=t,Ye=null,e=!0):e=!1),e||tn(t)),null;case 13:return Lv(e,t,a);case 4:return ce(t,t.stateNode.containerInfo),l=t.pendingProps,e===null?t.child=kn(t,null,l,a):nt(e,t,l,a),t.child;case 11:return Mv(e,t,t.type,t.pendingProps,a);case 7:return nt(e,t,t.pendingProps,a),t.child;case 8:return nt(e,t,t.pendingProps.children,a),t.child;case 12:return nt(e,t,t.pendingProps.children,a),t.child;case 10:return l=t.pendingProps,ul(t,t.type,l.value),nt(e,t,l.children,a),t.child;case 9:return n=t.type._context,l=t.pendingProps.children,ln(t),n=ct(n),l=l(n),t.flags|=1,nt(e,t,l,a),t.child;case 14:return Dv(e,t,t.type,t.pendingProps,a);case 15:return wv(e,t,t.type,t.pendingProps,a);case 19:return jv(e,t,a);case 31:return l=t.pendingProps,a=t.mode,l={mode:l.mode,children:l.children},e===null?(a=Eu(l,a),a.ref=t.ref,t.child=a,a.return=t,t=a):(a=wa(e.child,l),a.ref=t.ref,t.child=a,a.return=t,t=a),t;case 22:return zv(e,t,a);case 24:return ln(t),l=ct(We),e===null?(n=Qc(),n===null&&(n=Be,i=Vc(),n.pooledCache=i,i.refCount++,i!==null&&(n.pooledCacheLanes|=a),n=i),t.memoizedState={parent:l,cache:n},Zc(t),ul(t,We,n)):((e.lanes&a)!==0&&(Wc(e,t),Gr(t,null,null,a),Yr()),n=e.memoizedState,i=t.memoizedState,n.parent!==l?(n={parent:l,cache:l},t.memoizedState=n,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=n),ul(t,We,l)):(l=i.cache,ul(t,We,l),l!==n.cache&&Xc(t,[We],a,!0))),nt(e,t,t.pendingProps.children,a),t.child;case 29:throw t.pendingProps}throw Error(c(156,t.tag))}function ja(e){e.flags|=4}function Gv(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!Ph(t)){if(t=Kt.current,t!==null&&((Se&4194048)===Se?da!==null:(Se&62914560)!==Se&&(Se&536870912)===0||t!==da))throw qr=Kc,xd;e.flags|=8192}}function Tu(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?jl():536870912,e.lanes|=t,Wn|=t)}function Wr(e,t){if(!Ee)switch(e.tailMode){case"hidden":t=e.tail;for(var a=null;t!==null;)t.alternate!==null&&(a=t),t=t.sibling;a===null?e.tail=null:a.sibling=null;break;case"collapsed":a=e.tail;for(var l=null;a!==null;)a.alternate!==null&&(l=a),a=a.sibling;l===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:l.sibling=null}}function qe(e){var t=e.alternate!==null&&e.alternate.child===e.child,a=0,l=0;if(t)for(var n=e.child;n!==null;)a|=n.lanes|n.childLanes,l|=n.subtreeFlags&65011712,l|=n.flags&65011712,n.return=e,n=n.sibling;else for(n=e.child;n!==null;)a|=n.lanes|n.childLanes,l|=n.subtreeFlags,l|=n.flags,n.return=e,n=n.sibling;return e.subtreeFlags|=l,e.childLanes=a,t}function MS(e,t,a){var l=t.pendingProps;switch(qc(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return qe(t),null;case 1:return qe(t),null;case 3:return a=t.stateNode,l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),Ha(We),At(),a.pendingContext&&(a.context=a.pendingContext,a.pendingContext=null),(e===null||e.child===null)&&(wr(t)?ja(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,gd())),qe(t),null;case 26:return a=t.memoizedState,e===null?(ja(t),a!==null?(qe(t),Gv(t,a)):(qe(t),t.flags&=-16777217)):a?a!==e.memoizedState?(ja(t),qe(t),Gv(t,a)):(qe(t),t.flags&=-16777217):(e.memoizedProps!==l&&ja(t),qe(t),t.flags&=-16777217),null;case 27:Wa(t),a=te.current;var n=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==l&&ja(t);else{if(!l){if(t.stateNode===null)throw Error(c(166));return qe(t),null}e=$.current,wr(t)?bd(t):(e=Gh(n,l,a),t.stateNode=e,ja(t))}return qe(t),null;case 5:if(Wa(t),a=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==l&&ja(t);else{if(!l){if(t.stateNode===null)throw Error(c(166));return qe(t),null}if(e=$.current,wr(t))bd(t);else{switch(n=Uu(te.current),e){case 1:e=n.createElementNS("http://www.w3.org/2000/svg",a);break;case 2:e=n.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;default:switch(a){case"svg":e=n.createElementNS("http://www.w3.org/2000/svg",a);break;case"math":e=n.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;case"script":e=n.createElement("div"),e.innerHTML=" + + + +
+ + + \ No newline at end of file diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 00000000..bc4195fc --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,28 @@ + + + + + + + OP-SQLite Web Example + + + + +
+ + + + \ No newline at end of file diff --git a/example/web/index.web.tsx b/example/web/index.web.tsx new file mode 100644 index 00000000..68110419 --- /dev/null +++ b/example/web/index.web.tsx @@ -0,0 +1 @@ +import '../index.web'; diff --git a/src/Storage.web.ts b/src/Storage.web.ts new file mode 100644 index 00000000..f851c358 --- /dev/null +++ b/src/Storage.web.ts @@ -0,0 +1,82 @@ +import { openAsync } from './functions.web'; +import { type DB } from './types'; + +type StorageOptions = { + location?: string; + encryptionKey?: string; +}; + +export class Storage { + private dbPromise: Promise; + + constructor(options: StorageOptions) { + this.dbPromise = (async () => { + const db = await openAsync({ ...options, name: '__opsqlite_storage.sqlite' }); + await db.execute( + 'CREATE TABLE IF NOT EXISTS storage (key TEXT PRIMARY KEY, value TEXT)' + ); + return db; + })(); + } + + private async getDb(): Promise { + return this.dbPromise; + } + + async getItem(key: string): Promise { + const db = await this.getDb(); + const result = await db.execute('SELECT value FROM storage WHERE key = ?', [key]); + + const value = result.rows[0]?.value; + if (typeof value !== 'undefined' && typeof value !== 'string') { + throw new Error('Value must be a string or undefined'); + } + + return value; + } + + getItemSync(_key: string): string | undefined { + throw new Error('[op-sqlite] Storage sync APIs are not supported on web.'); + } + + async setItem(key: string, value: string): Promise { + const db = await this.getDb(); + await db.execute('INSERT OR REPLACE INTO storage (key, value) VALUES (?, ?)', [ + key, + value, + ]); + } + + setItemSync(_key: string, _value: string): void { + throw new Error('[op-sqlite] Storage sync APIs are not supported on web.'); + } + + async removeItem(key: string): Promise { + const db = await this.getDb(); + await db.execute('DELETE FROM storage WHERE key = ?', [key]); + } + + removeItemSync(_key: string): void { + throw new Error('[op-sqlite] Storage sync APIs are not supported on web.'); + } + + async clear(): Promise { + const db = await this.getDb(); + await db.execute('DELETE FROM storage'); + } + + clearSync(): void { + throw new Error('[op-sqlite] Storage sync APIs are not supported on web.'); + } + + async getAllKeys(): Promise { + const db = await this.getDb(); + const result = await db.execute('SELECT key FROM storage'); + + return result.rows.map((row: any) => String(row.key)); + } + + delete(): void { + throw new Error('[op-sqlite] Storage.delete() is not supported on web.'); + } +} diff --git a/src/functions.ts b/src/functions.ts index e0f7c386..399888ee 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -105,6 +105,9 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB { setReservedBytes: db.setReservedBytes, getReservedBytes: db.getReservedBytes, close: db.close, + closeAsync: async () => { + db.close(); + }, flushPendingReactiveQueries: db.flushPendingReactiveQueries, executeBatch: async ( commands: SQLBatchTuple[] @@ -419,6 +422,18 @@ export const open = (params: { return enhancedDb; }; +/** + * Async wrapper around open(). + * Useful for cross-platform code that also targets web where openAsync() is required. + */ +export const openAsync = async (params: { + name: string; + location?: string; + encryptionKey?: string; +}): Promise => { + return open(params); +}; + /** * Moves the database from the assets folder to the default path (check the docs) or to a custom path * It DOES NOT OVERWRITE the database if it already exists in the destination path diff --git a/src/functions.web.ts b/src/functions.web.ts new file mode 100644 index 00000000..3aa6345a --- /dev/null +++ b/src/functions.web.ts @@ -0,0 +1,498 @@ +import type { + _InternalDB, + _PendingTransaction, + BatchQueryResult, + DB, + DBParams, + FileLoadResult, + OPSQLiteProxy, + PreparedStatement, + QueryResult, + Scalar, + SQLBatchTuple, + Transaction, +} from './types'; + +type WorkerPromiser = (type: string, args?: Record) => Promise; + +const WEB_ONLY_SYNC_ERROR = + '[op-sqlite] Web backend is async-only. Use openAsync() and async methods like execute().'; + +function throwSyncApiError(method: string): never { + throw new Error(`${WEB_ONLY_SYNC_ERROR} Called sync method: ${method}().`); +} + +function toNumber(value: unknown): number | undefined { + if (value == null) { + return undefined; + } + + if (typeof value === 'bigint') { + const asNumber = Number(value); + return Number.isFinite(asNumber) ? asNumber : undefined; + } + + if (typeof value === 'number') { + return Number.isFinite(value) ? value : undefined; + } + + return undefined; +} + +function ensureSingleStatement(sql: string): void { + // Web worker executes the full SQL string while native executes only the first prepared statement. + // We warn here so callers can keep behavior consistent across platforms when needed. + if (sql.includes(';')) { + const trimmed = sql.trim(); + if (!trimmed.endsWith(';') || trimmed.slice(0, -1).includes(';')) { + console.warn( + '[op-sqlite] Web execute() runs full SQL strings. Avoid multi-statement SQL for parity with native first-statement behavior.' + ); + } + } +} + +let promiserPromise: Promise | null = null; + +async function getPromiser(): Promise { + if (!promiserPromise) { + promiserPromise = (async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore TS declaration is provided via src/web/sqlite-wasm.d.ts + const mod = await import('./web/sqlite-wasm/index.mjs'); + const makePromiser = mod.sqlite3Worker1Promiser as ( + config?: unknown + ) => Promise; + + const WebWorker = (globalThis as any).Worker; + if (!WebWorker) { + throw new Error('[op-sqlite] Web Worker API is not available in this environment.'); + } + + return makePromiser({ + worker: () => + new WebWorker(new URL('./web/sqlite-wasm/sqlite3-worker1.mjs', import.meta.url), { + type: 'module', + }), + }); + })(); + } + + return promiserPromise; +} + +async function ensureOpfs(promiser: WorkerPromiser): Promise { + const config = await promiser('config-get', {}); + const vfsList = config?.result?.vfsList; + + if (!Array.isArray(vfsList) || !vfsList.includes('opfs')) { + throw new Error( + '[op-sqlite] OPFS is required on web for persistence. Ensure COOP/COEP headers are set and OPFS is available in this browser.' + ); + } +} + +async function executeWorker( + promiser: WorkerPromiser, + dbId: string, + query: string, + params?: Scalar[] +): Promise { + ensureSingleStatement(query); + + const response = await promiser('exec', { + dbId, + sql: query, + bind: params, + rowMode: 'object', + resultRows: [], + columnNames: [], + returnValue: 'resultRows', + }); + + const result = response?.result; + const rows = Array.isArray(result?.resultRows) + ? (result.resultRows as Array>) + : Array.isArray(result) + ? (result as Array>) + : []; + const columnNames = Array.isArray(result?.columnNames) + ? (result.columnNames as string[]) + : rows.length > 0 + ? Object.keys(rows[0] ?? {}) + : []; + + const rowsAffected = toNumber(result?.changeCount) ?? 0; + const insertId = toNumber(result?.lastInsertRowId); + + return { + rowsAffected, + insertId, + rows, + columnNames, + }; +} + +function enhanceWebDb( + db: _InternalDB, + options: { name?: string; location?: string } +): DB { + const lock = { + queue: [] as _PendingTransaction[], + inProgress: false, + }; + + const startNextTransaction = () => { + if (lock.inProgress || lock.queue.length === 0) { + return; + } + + lock.inProgress = true; + const tx = lock.queue.shift(); + if (!tx) { + throw new Error('Could not get an operation on database'); + } + + setTimeout(() => { + tx.start(); + }, 0); + }; + + const withTransactionLock = async (work: () => Promise): Promise => { + return new Promise((resolve, reject) => { + const tx: _PendingTransaction = { + start: () => { + work() + .then(resolve) + .catch(reject) + .finally(() => { + lock.inProgress = false; + startNextTransaction(); + }); + }, + }; + + lock.queue.push(tx); + startNextTransaction(); + }); + }; + + const unsupported = (method: string) => () => throwSyncApiError(method); + + const enhancedDb: DB = { + close: unsupported('close'), + closeAsync: async () => { + await db.closeAsync?.(); + }, + delete: unsupported('delete'), + attach: unsupported('attach'), + detach: unsupported('detach'), + transaction: async (fn: (tx: Transaction) => Promise): Promise => { + return withTransactionLock(async () => { + let finalized = false; + + const commit = async (): Promise => { + if (finalized) { + throw new Error( + `OP-Sqlite Error: Database: ${options.name}. Cannot execute query on finalized transaction` + ); + } + + const res = await enhancedDb.execute('COMMIT;'); + finalized = true; + return res; + }; + + const rollback = (): QueryResult => { + throwSyncApiError('rollback'); + }; + + const execute = async (query: string, params?: Scalar[]) => { + if (finalized) { + throw new Error( + `OP-Sqlite Error: Database: ${options.name}. Cannot execute query on finalized transaction` + ); + } + + return enhancedDb.execute(query, params); + }; + + await enhancedDb.execute('BEGIN TRANSACTION;'); + + try { + await fn({ + execute, + commit, + rollback, + }); + + if (!finalized) { + await commit(); + } + } catch (error) { + if (!finalized) { + await enhancedDb.execute('ROLLBACK;'); + } + + throw error; + } + }); + }, + executeSync: unsupported('executeSync'), + execute: db.execute, + executeWithHostObjects: db.execute, + executeBatch: async (commands: SQLBatchTuple[]): Promise => { + await withTransactionLock(async () => { + await db.execute('BEGIN TRANSACTION;'); + + try { + for (const command of commands) { + const [sql, bind] = command; + + if (!bind) { + await db.execute(sql); + continue; + } + + if (Array.isArray(bind[0])) { + for (const rowBind of bind as Scalar[][]) { + await db.execute(sql, rowBind); + } + } else { + await db.execute(sql, bind as Scalar[]); + } + } + + await db.execute('COMMIT;'); + } catch (error) { + await db.execute('ROLLBACK;'); + throw error; + } + }); + + return { + rowsAffected: 0, + }; + }, + loadFile: async (_location: string): Promise => { + throw new Error('[op-sqlite] loadFile() is not supported on web.'); + }, + updateHook: () => { + throw new Error('[op-sqlite] updateHook() is not supported on web.'); + }, + commitHook: () => { + throw new Error('[op-sqlite] commitHook() is not supported on web.'); + }, + rollbackHook: () => { + throw new Error('[op-sqlite] rollbackHook() is not supported on web.'); + }, + prepareStatement: (query: string): PreparedStatement => { + let currentParams: Scalar[] = []; + + return { + bind: async (params: Scalar[]) => { + currentParams = params; + }, + bindSync: unsupported('bindSync'), + execute: async () => { + return db.execute(query, currentParams); + }, + }; + }, + loadExtension: unsupported('loadExtension'), + executeRaw: db.executeRaw, + executeRawSync: unsupported('executeRawSync'), + getDbPath: unsupported('getDbPath'), + reactiveExecute: unsupported('reactiveExecute'), + sync: unsupported('sync'), + setReservedBytes: unsupported('setReservedBytes'), + getReservedBytes: unsupported('getReservedBytes'), + flushPendingReactiveQueries: async () => {}, + }; + + return enhancedDb; +} + +async function createWebDb(params: { + name: string; + location?: string; + encryptionKey?: string; +}): Promise<_InternalDB> { + if (params.encryptionKey) { + throw new Error('[op-sqlite] SQLCipher is not supported on web.'); + } + + const promiser = await getPromiser(); + await ensureOpfs(promiser); + + const filename = `file:${params.name}?vfs=opfs`; + const opened = await promiser('open', { + filename, + }); + + const dbId = opened?.dbId || opened?.result?.dbId; + if (!dbId || typeof dbId !== 'string') { + throw new Error('[op-sqlite] Failed to open web sqlite database.'); + } + + return { + close: () => { + throwSyncApiError('close'); + }, + closeAsync: async () => { + await promiser('close', { + dbId, + }); + }, + delete: () => { + throwSyncApiError('delete'); + }, + attach: () => { + throw new Error('[op-sqlite] attach() is not supported on web.'); + }, + detach: () => { + throw new Error('[op-sqlite] detach() is not supported on web.'); + }, + transaction: async () => { + throw new Error('[op-sqlite] transaction() must be called on an opened DB object.'); + }, + executeSync: () => { + throwSyncApiError('executeSync'); + }, + execute: async (query: string, bind?: Scalar[]) => { + return executeWorker(promiser, dbId, query, bind); + }, + executeWithHostObjects: async (query: string, bind?: Scalar[]) => { + return executeWorker(promiser, dbId, query, bind); + }, + executeBatch: async (_commands: SQLBatchTuple[]) => { + throw new Error('[op-sqlite] executeBatch() must be called on an opened DB object.'); + }, + loadFile: async (_location: string) => { + throw new Error('[op-sqlite] loadFile() is not supported on web.'); + }, + updateHook: () => { + throw new Error('[op-sqlite] updateHook() is not supported on web.'); + }, + commitHook: () => { + throw new Error('[op-sqlite] commitHook() is not supported on web.'); + }, + rollbackHook: () => { + throw new Error('[op-sqlite] rollbackHook() is not supported on web.'); + }, + prepareStatement: (_query: string) => { + throw new Error('[op-sqlite] prepareStatement() must be called on an opened DB object.'); + }, + loadExtension: () => { + throw new Error('[op-sqlite] loadExtension() is not supported on web.'); + }, + executeRaw: async (query: string, bind?: Scalar[]) => { + ensureSingleStatement(query); + + const response = await promiser('exec', { + dbId, + sql: query, + bind, + rowMode: 'array', + resultRows: [], + returnValue: 'resultRows', + }); + + const result = response?.result; + const rows = result?.resultRows ?? result; + return Array.isArray(rows) ? rows : []; + }, + executeRawSync: () => { + throwSyncApiError('executeRawSync'); + }, + getDbPath: () => { + throwSyncApiError('getDbPath'); + }, + reactiveExecute: () => { + throw new Error('[op-sqlite] reactiveExecute() is not supported on web.'); + }, + sync: () => { + throwSyncApiError('sync'); + }, + setReservedBytes: () => { + throwSyncApiError('setReservedBytes'); + }, + getReservedBytes: () => { + throwSyncApiError('getReservedBytes'); + }, + flushPendingReactiveQueries: async () => {}, + }; +} + +/** + * Open a connection to a local sqlite database on web. + * Web is async-only: use openAsync() and async methods like execute(). + */ +export const openAsync = async (params: { + name: string; + location?: string; + encryptionKey?: string; +}): Promise => { + const db = await createWebDb(params); + return enhanceWebDb(db, params); +}; + +export const open = (_params: { + name: string; + location?: string; + encryptionKey?: string; +}): DB => { + throwSyncApiError('open'); +}; + +export const openSync = (_params: { + url: string; + authToken: string; + name: string; + location?: string; + libsqlSyncInterval?: number; + libsqlOffline?: boolean; + encryptionKey?: string; + remoteEncryptionKey?: string; +}): DB => { + throwSyncApiError('openSync'); +}; + +export const openRemote = (_params: { + url: string; + authToken: string; +}): DB => { + throw new Error('[op-sqlite] openRemote() is not supported on web.'); +}; + +export const moveAssetsDatabase = async (_args: { + filename: string; + path?: string; + overwrite?: boolean; +}): Promise => { + throw new Error('[op-sqlite] moveAssetsDatabase() is not supported on web.'); +}; + +export const getDylibPath = (_bundle: string, _name: string): string => { + throw new Error('[op-sqlite] getDylibPath() is not supported on web.'); +}; + +export const isSQLCipher = (): boolean => { + return false; +}; + +export const isLibsql = (): boolean => { + return false; +}; + +export const isIOSEmbedded = (): boolean => { + return false; +}; + +/** + * @deprecated Use `isIOSEmbedded` instead. This alias will be removed in a future release. + */ +export const isIOSEmbeeded = isIOSEmbedded; + +// Web does not expose the native JSI proxy object. +export const OPSQLite = {} as OPSQLiteProxy; diff --git a/src/index.web.ts b/src/index.web.ts new file mode 100644 index 00000000..5b5b2a55 --- /dev/null +++ b/src/index.web.ts @@ -0,0 +1,24 @@ +export * from './functions.web'; +export { Storage } from './Storage.web'; +export type { + Scalar, + QueryResult, + ColumnMetadata, + SQLBatchTuple, + UpdateHookOperation, + BatchQueryResult, + FileLoadResult, + Transaction, + _PendingTransaction, + PreparedStatement, + _InternalDB, + DB, + DBParams, + OPSQLiteProxy, +} from './types'; + +export const IOS_DOCUMENT_PATH = ''; +export const IOS_LIBRARY_PATH = ''; +export const ANDROID_DATABASE_PATH = ''; +export const ANDROID_FILES_PATH = ''; +export const ANDROID_EXTERNAL_FILES_PATH = ''; diff --git a/src/types.ts b/src/types.ts index 63bc446f..07b42f5f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -101,6 +101,7 @@ export type PreparedStatement = { export type _InternalDB = { close: () => void; + closeAsync?: () => Promise; delete: (location?: string) => void; attach: (params: { secondaryDbFileName: string; @@ -151,6 +152,7 @@ export type _InternalDB = { export type DB = { close: () => void; + closeAsync: () => Promise; delete: (location?: string) => void; attach: (params: { secondaryDbFileName: string; diff --git a/src/web/sqlite-wasm.d.ts b/src/web/sqlite-wasm.d.ts new file mode 100644 index 00000000..30c419e1 --- /dev/null +++ b/src/web/sqlite-wasm.d.ts @@ -0,0 +1,3 @@ +declare module './sqlite-wasm/index.mjs' { + export const sqlite3Worker1Promiser: (config?: unknown) => Promise; +} diff --git a/src/web/sqlite-wasm/index.mjs b/src/web/sqlite-wasm/index.mjs new file mode 100644 index 00000000..17ae70cc --- /dev/null +++ b/src/web/sqlite-wasm/index.mjs @@ -0,0 +1,15645 @@ +//#region src/bin/sqlite3-worker1-promiser.mjs +/** +Configures an sqlite3 Worker API #1 Worker such that it can be +manipulated via a Promise-based interface and returns a factory +function which returns Promises for communicating with the worker. +This proxy has an _almost_ identical interface to the normal +worker API, with any exceptions documented below. + +It requires a configuration object with the following properties: + +- `worker` (required): a Worker instance which loads +`sqlite3-worker1.js` or a functional equivalent. Note that the +promiser factory replaces the worker.onmessage property. This +config option may alternately be a function, in which case this +function re-assigns this property with the result of calling that +function, enabling delayed instantiation of a Worker. + +- `onready` (optional, but...): this callback is called with no +arguments when the worker fires its initial +'sqlite3-api'/'worker1-ready' message, which it does when +sqlite3.initWorker1API() completes its initialization. This is the +simplest way to tell the worker to kick off work at the earliest +opportunity, and the only way to know when the worker module has +completed loading. The irony of using a callback for this, instead +of returning a promise from sqlite3Worker1Promiser() is not lost on +the developers: see sqlite3Worker1Promiser.v2() which uses a +Promise instead. + +- `onunhandled` (optional): a callback which gets passed the +message event object for any worker.onmessage() events which +are not handled by this proxy. Ideally that "should" never +happen, as this proxy aims to handle all known message types. + +- `generateMessageId` (optional): a function which, when passed an +about-to-be-posted message object, generates a _unique_ message ID +for the message, which this API then assigns as the messageId +property of the message. It _must_ generate unique IDs on each call +so that dispatching can work. If not defined, a default generator +is used (which should be sufficient for most or all cases). + +- `debug` (optional): a console.debug()-style function for logging +information about messages. + +This function returns a stateful factory function with the +following interfaces: + +- Promise function(messageType, messageArgs) +- Promise function({message object}) + +The first form expects the "type" and "args" values for a Worker +message. The second expects an object in the form {type:..., +args:...} plus any other properties the client cares to set. This +function will always set the `messageId` property on the object, +even if it's already set, and will set the `dbId` property to the +current database ID if it is _not_ set in the message object. + +The function throws on error. + +The function installs a temporary message listener, posts a +message to the configured Worker, and handles the message's +response via the temporary message listener. The then() callback +of the returned Promise is passed the `message.data` property from +the resulting message, i.e. the payload from the worker, stripped +of the lower-level event state which the onmessage() handler +receives. + +Example usage: + +``` +const config = {...}; +const sq3Promiser = sqlite3Worker1Promiser(config); +sq3Promiser('open', {filename:"/foo.db"}).then(function(msg){ +console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} +}); +sq3Promiser({type:'close'}).then((msg)=>{ +console.log("close response",msg); // => {type:'close', result: {filename:'/foo.db'}, ...} +}); +``` + +Differences from Worker API #1: + +- exec's {callback: STRING} option does not work via this +interface (it triggers an exception), but {callback: function} +does and works exactly like the STRING form does in the Worker: +the callback is called one time for each row of the result set, +passed the same worker message format as the worker API emits: + +{ +type:typeString, +row:VALUE, +rowNumber:1-based-#, +columnNames: array +} + +Where `typeString` is an internally-synthesized message type string +used temporarily for worker message dispatching. It can be ignored +by all client code except that which tests this API. The `row` +property contains the row result in the form implied by the +`rowMode` option (defaulting to `'array'`). The `rowNumber` is a +1-based integer value incremented by 1 on each call into the +callback. + +At the end of the result set, the same event is fired with +(row=undefined, rowNumber=null) to indicate that the end of the +result set has been reached. The rows arrive via worker-posted +messages, with all the implications of that. + +Notable shortcomings: + +- "v1" of this this API is not suitable for use as an ESM module +because ESM worker modules were not widely supported when it was +developed. For use as an ESM module, see the "v2" interface later +on in this file. +*/ +globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig) { + if (1 === arguments.length && "function" === typeof arguments[0]) { + const f = config; + config = Object.assign(Object.create(null), callee.defaultConfig); + config.onready = f; + } else config = Object.assign(Object.create(null), callee.defaultConfig, config); + const handlerMap = Object.create(null); + const noop = function() {}; + const err = config.onerror || noop; + const debug = config.debug || noop; + const idTypeMap = config.generateMessageId ? void 0 : Object.create(null); + const genMsgId = config.generateMessageId || function(msg) { + return msg.type + "#" + (idTypeMap[msg.type] = (idTypeMap[msg.type] || 0) + 1); + }; + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + if (!config.worker) config.worker = callee.defaultConfig.worker; + if ("function" === typeof config.worker) config.worker = config.worker(); + let dbId; + let promiserFunc; + config.worker.onmessage = function(ev) { + ev = ev.data; + debug("worker1.onmessage", ev); + let msgHandler = handlerMap[ev.messageId]; + if (!msgHandler) { + if (ev && "sqlite3-api" === ev.type && "worker1-ready" === ev.result) { + if (config.onready) config.onready(promiserFunc); + return; + } + msgHandler = handlerMap[ev.type]; + if (msgHandler && msgHandler.onrow) { + msgHandler.onrow(ev); + return; + } + if (config.onunhandled) config.onunhandled(arguments[0]); + else err("sqlite3Worker1Promiser() unhandled worker message:", ev); + return; + } + delete handlerMap[ev.messageId]; + switch (ev.type) { + case "error": + msgHandler.reject(ev); + return; + case "open": + if (!dbId) dbId = ev.dbId; + break; + case "close": + if (ev.dbId === dbId) dbId = void 0; + break; + default: break; + } + try { + msgHandler.resolve(ev); + } catch (e) { + msgHandler.reject(e); + } + }; + return promiserFunc = function() { + let msg; + if (1 === arguments.length) msg = arguments[0]; + else if (2 === arguments.length) { + msg = Object.create(null); + msg.type = arguments[0]; + msg.args = arguments[1]; + msg.dbId = msg.args.dbId; + } else toss("Invalid arguments for sqlite3Worker1Promiser()-created factory."); + if (!msg.dbId && msg.type !== "open") msg.dbId = dbId; + msg.messageId = genMsgId(msg); + msg.departureTime = performance.now(); + const proxy = Object.create(null); + proxy.message = msg; + let rowCallbackId; + if ("exec" === msg.type && msg.args) { + if ("function" === typeof msg.args.callback) { + rowCallbackId = msg.messageId + ":row"; + proxy.onrow = msg.args.callback; + msg.args.callback = rowCallbackId; + handlerMap[rowCallbackId] = proxy; + } else if ("string" === typeof msg.args.callback) toss("exec callback may not be a string when using the Promise interface."); + } + let p = new Promise(function(resolve, reject) { + proxy.resolve = resolve; + proxy.reject = reject; + handlerMap[msg.messageId] = proxy; + debug("Posting", msg.type, "message to Worker dbId=" + (dbId || "default") + ":", msg); + config.worker.postMessage(msg); + }); + if (rowCallbackId) p = p.finally(() => delete handlerMap[rowCallbackId]); + return p; + }; +}; +globalThis.sqlite3Worker1Promiser.defaultConfig = { + worker: function() { + return new Worker(new URL("sqlite3-worker1.mjs", import.meta.url), { type: "module" }); + }, + onerror: (...args) => console.error("sqlite3Worker1Promiser():", ...args) +}; +/** +sqlite3Worker1Promiser.v2(), added in 3.46, works identically to +sqlite3Worker1Promiser() except that it returns a Promise instead +of relying an an onready callback in the config object. The Promise +resolves to the same factory function which +sqlite3Worker1Promiser() returns. + +If config is-a function or is an object which contains an onready +function, that function is replaced by a proxy which will resolve +after calling the original function and will reject if that +function throws. +*/ +globalThis.sqlite3Worker1Promiser.v2 = function callee(config = callee.defaultConfig) { + let oldFunc; + if ("function" == typeof config) { + oldFunc = config; + config = {}; + } else if ("function" === typeof config?.onready) { + oldFunc = config.onready; + delete config.onready; + } + const promiseProxy = Object.create(null); + config = Object.assign(config || Object.create(null), { onready: async function(func) { + try { + if (oldFunc) await oldFunc(func); + promiseProxy.resolve(func); + } catch (e) { + promiseProxy.reject(e); + } + } }); + const p = new Promise(function(resolve, reject) { + promiseProxy.resolve = resolve; + promiseProxy.reject = reject; + }); + try { + this.original(config); + } catch (e) { + promiseProxy.reject(e); + } + return p; +}.bind({ original: sqlite3Worker1Promiser }); +globalThis.sqlite3Worker1Promiser.v2.defaultConfig = globalThis.sqlite3Worker1Promiser.defaultConfig; +/** +When built as a module, we export sqlite3Worker1Promiser.v2() +instead of sqlite3Worker1Promise() because (A) its interface is more +conventional for ESM usage and (B) the ESM export option for this +API did not exist until v2 was created, so there's no backwards +incompatibility. +*/ +var sqlite3_worker1_promiser_default = sqlite3Worker1Promiser.v2; +delete globalThis.sqlite3Worker1Promiser; +//#endregion +//#region src/bin/sqlite3-bundler-friendly.mjs +/* @preserve +** +** LICENSE for the sqlite3 WebAssembly/JavaScript APIs. +** +** This bundle (typically released as sqlite3.js or sqlite3.mjs) +** is an amalgamation of JavaScript source code from two projects: +** +** 1) https://emscripten.org: the Emscripten "glue code" is covered by +** the terms of the MIT license and University of Illinois/NCSA +** Open Source License, as described at: +** +** https://emscripten.org/docs/introducing_emscripten/emscripten_license.html +** +** 2) https://sqlite.org: all code and documentation labeled as being +** from this source are released under the same terms as the sqlite3 +** C library: +** +** 2022-10-16 +** +** The author disclaims copyright to this source code. In place of a +** legal notice, here is a blessing: +** +** * May you do good and not evil. +** * May you find forgiveness for yourself and forgive others. +** * May you share freely, never taking more than you give. +*/ +/* @preserve +** This code was built from sqlite3 version... +** +** SQLITE_VERSION "3.52.0" +** SQLITE_VERSION_NUMBER 3052000 +** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** +** Emscripten SDK: 5.0.0 +*/ +async function sqlite3InitModule(moduleArg = {}) { + var moduleRtn; + var Module = moduleArg; + var ENVIRONMENT_IS_WEB = !!globalThis.window; + var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; + globalThis.process?.versions?.node && globalThis.process?.type; + var thisProgram = "./this.program"; + var _scriptName = import.meta.url; + 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) { + try { + scriptDirectory = new URL(".", _scriptName).href; + } catch {} + 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 = async (url) => { + var response = await fetch(url, { credentials: "same-origin" }); + if (response.ok) return response.arrayBuffer(); + throw new Error(response.status + " : " + response.url); + }; + } + var out = console.log.bind(console); + var err = console.error.bind(console); + var wasmBinary; + var ABORT = false, readyPromiseResolve, readyPromiseReject, HEAP8, HEAPU8, HEAP16, HEAP32, HEAPU32, HEAP64; + var runtimeInitialized = false; + function updateMemoryViews() { + var b = wasmMemory.buffer; + HEAP8 = new Int8Array(b); + HEAP16 = new Int16Array(b); + HEAPU8 = new Uint8Array(b); + new Uint16Array(b); + HEAP32 = new Int32Array(b); + HEAPU32 = new Uint32Array(b); + new Float32Array(b); + new Float64Array(b); + HEAP64 = new BigInt64Array(b); + new BigUint64Array(b); + } + function initMemory() { + if (Module["wasmMemory"]) wasmMemory = Module["wasmMemory"]; + else { + var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 8388608; + /** @suppress {checkTypes} */ + wasmMemory = new WebAssembly.Memory({ + "initial": INITIAL_MEMORY / 65536, + "maximum": 32768 + }); + } + updateMemoryViews(); + } + function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; + while (Module["preRun"].length) addOnPreRun(Module["preRun"].shift()); + } + callRuntimeCallbacks(onPreRuns); + } + function initRuntime() { + runtimeInitialized = true; + if (!Module["noFSInit"] && !FS.initialized) FS.init(); + TTY.init(); + wasmExports["__wasm_call_ctors"](); + FS.ignorePermissions = false; + } + function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; + while (Module["postRun"].length) addOnPostRun(Module["postRun"].shift()); + } + callRuntimeCallbacks(onPostRuns); + } + /** @param {string|number=} what */ + function abort(what) { + Module["onAbort"]?.(what); + what = "Aborted(" + what + ")"; + err(what); + ABORT = true; + what += ". Build with -sASSERTIONS for more info."; + /** @suppress {checkTypes} */ + var e = new WebAssembly.RuntimeError(what); + readyPromiseReject?.(e); + throw e; + } + var wasmBinaryFile; + function findWasmBinary() { + if (Module["locateFile"]) return locateFile("sqlite3.wasm"); + return new URL("sqlite3.wasm", import.meta.url).href; + } + 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"; + } + async function getWasmBinary(binaryFile) { + if (!wasmBinary) try { + var response = await readAsync(binaryFile); + return new Uint8Array(response); + } catch {} + return getBinarySync(binaryFile); + } + async function instantiateArrayBuffer(binaryFile, imports) { + try { + var binary = await getWasmBinary(binaryFile); + return await WebAssembly.instantiate(binary, imports); + } catch (reason) { + err(`failed to asynchronously prepare wasm: ${reason}`); + abort(reason); + } + } + async function instantiateAsync(binary, binaryFile, imports) { + if (!binary) try { + var response = fetch(binaryFile, { credentials: "same-origin" }); + return await WebAssembly.instantiateStreaming(response, imports); + } catch (reason) { + err(`wasm streaming compile failed: ${reason}`); + err("falling back to ArrayBuffer instantiation"); + } + return instantiateArrayBuffer(binaryFile, imports); + } + function getWasmImports() { + return { + "env": wasmImports, + "wasi_snapshot_preview1": wasmImports + }; + } + async function createWasm() { + /** @param {WebAssembly.Module=} module*/ + function receiveInstance(instance, module) { + wasmExports = instance.exports; + assignWasmExports(wasmExports); + return wasmExports; + } + function receiveInstantiationResult(result) { + return receiveInstance(result["instance"]); + } + var info = getWasmImports(); + if (Module["instantiateWasm"]) return new Promise((resolve, reject) => { + Module["instantiateWasm"](info, (inst, mod) => { + resolve(receiveInstance(inst, mod)); + }); + }); + wasmBinaryFile ??= findWasmBinary(); + return receiveInstantiationResult(await instantiateAsync(wasmBinary, wasmBinaryFile, info)); + } + var callRuntimeCallbacks = (callbacks) => { + while (callbacks.length > 0) callbacks.shift()(Module); + }; + var onPostRuns = []; + var addOnPostRun = (cb) => onPostRuns.push(cb); + var onPreRuns = []; + var addOnPreRun = (cb) => onPreRuns.push(cb); + var wasmMemory; + var PATH = { + isAbs: (path) => path.charAt(0) === "/", + splitPath: (filename) => { + return /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.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.slice(-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.slice(0, -1); + return root + dir; + }, + basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1], + join: (...paths) => PATH.normalize(paths.join("/")), + join2: (l, r) => PATH.normalize(l + "/" + r) + }; + var initRandomFill = () => { + return (view) => crypto.getRandomValues(view); + }; + 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).slice(1); + to = PATH_FS.resolve(to).slice(1); + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) if (arr[start] !== "") break; + var end = arr.length - 1; + for (; end >= 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 < length; i++) if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) outputParts.push(".."); + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join("/"); + } + }; + var UTF8Decoder = new TextDecoder(); + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; + return idx; + }; + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number=} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { + var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); + return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr))); + }; + var FS_stdin_getChar_buffer = []; + var lengthBytesUTF8 = (str) => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + var c = str.charCodeAt(i); + if (c <= 127) len++; + else if (c <= 2047) len += 2; + else if (c >= 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 < str.length; ++i) { + var u = str.codePointAt(i); + 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; + i++; + } + } + heap[outIdx] = 0; + return outIdx - startIdx; + }; + /** @type {function(string, boolean=, number=)} */ + var 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 (globalThis.window?.prompt) { + result = window.prompt("Input: "); + if (result !== null) result += "\n"; + } + 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; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); + if (result === null || result === void 0) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) stream.node.atime = Date.now(); + return bytesRead; + }, + write(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) throw new FS.ErrnoError(60); + try { + for (var i = 0; i < length; i++) stream.tty.ops.put_char(stream.tty, buffer[offset + i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) stream.node.mtime = stream.node.ctime = Date.now(); + return i; + } + }, + default_tty_ops: { + get_char(tty) { + return FS_stdin_getChar(); + }, + put_char(tty, val) { + if (val === null || val === 10) { + out(UTF8ArrayToString(tty.output)); + tty.output = []; + } else if (val != 0) tty.output.push(val); + }, + fsync(tty) { + if (tty.output?.length > 0) { + out(UTF8ArrayToString(tty.output)); + 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)); + tty.output = []; + } else if (val != 0) tty.output.push(val); + }, + fsync(tty) { + if (tty.output?.length > 0) { + err(UTF8ArrayToString(tty.output)); + tty.output = []; + } + } + } + }; + var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); + var alignMemory = (size, alignment) => { + return Math.ceil(size / alignment) * alignment; + }; + var mmapAlloc = (size) => { + size = alignMemory(size, 65536); + var ptr = _emscripten_builtin_memalign(65536, size); + if (ptr) zeroMemory(ptr, size); + return ptr; + }; + var MEMFS = { + ops_table: null, + mount(mount) { + return MEMFS.createNode(null, "/", 16895, 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, + 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.atime = node.mtime = node.ctime = Date.now(); + if (parent) { + parent.contents[name] = node; + parent.atime = parent.mtime = parent.ctime = node.atime; + } + 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; + newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 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.atime); + attr.mtime = new Date(node.mtime); + attr.ctime = new Date(node.ctime); + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr(node, attr) { + for (const key of [ + "mode", + "atime", + "mtime", + "ctime" + ]) if (attr[key] != null) node[key] = attr[key]; + if (attr.size !== void 0) MEMFS.resizeFileStorage(node, attr.size); + }, + lookup(parent, name) { + if (!MEMFS.doesNotExistError) { + MEMFS.doesNotExistError = new FS.ErrnoError(44); + /** @suppress {checkTypes} */ + MEMFS.doesNotExistError.stack = ""; + } + throw MEMFS.doesNotExistError; + }, + mknod(parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + }, + rename(old_node, new_dir, new_name) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (new_node) { + if (FS.isDir(old_node.mode)) for (var i in new_node.contents) throw new FS.ErrnoError(55); + FS.hashRemoveNode(new_node); + } + delete old_node.parent.contents[old_node.name]; + new_dir.contents[new_name] = old_node; + old_node.name = new_name; + new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); + }, + unlink(parent, name) { + delete parent.contents[name]; + parent.ctime = parent.mtime = Date.now(); + }, + rmdir(parent, name) { + for (var i in FS.lookupNode(parent, name).contents) throw new FS.ErrnoError(55); + delete parent.contents[name]; + parent.ctime = parent.mtime = Date.now(); + }, + readdir(node) { + return [ + ".", + "..", + ...Object.keys(node.contents) + ]; + }, + symlink(parent, newname, oldpath) { + var node = MEMFS.createNode(parent, newname, 41471, 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; i < size; i++) buffer[offset + i] = contents[position + i]; + return size; + }, + write(stream, buffer, offset, length, position, canOwn) { + if (buffer.buffer === HEAP8.buffer) canOwn = false; + if (!length) return 0; + var node = stream.node; + node.mtime = node.ctime = Date.now(); + if (buffer.subarray && (!node.contents || node.contents.subarray)) { + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + return length; + } else if (node.usedBytes === 0 && position === 0) { + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + return length; + } else if (position + length <= node.usedBytes) { + node.contents.set(buffer.subarray(offset, offset + length), position); + return length; + } + } + MEMFS.expandFileStorage(node, position + length); + if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); + else for (var i = 0; i < length; i++) node.contents[position + i] = buffer[offset + i]; + node.usedBytes = Math.max(node.usedBytes, position + length); + return length; + }, + llseek(stream, offset, whence) { + var position = offset; + if (whence === 1) position += stream.position; + else if (whence === 2) { + if (FS.isFile(stream.node.mode)) position += stream.node.usedBytes; + } + if (position < 0) throw new FS.ErrnoError(28); + return position; + }, + mmap(stream, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43); + var ptr; + var allocated; + var contents = stream.node.contents; + if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { + allocated = false; + ptr = contents.byteOffset; + } else { + allocated = true; + ptr = mmapAlloc(length); + if (!ptr) throw new FS.ErrnoError(48); + if (contents) { + if (position > 0 || position + length < contents.length) if (contents.subarray) contents = contents.subarray(position, position + length); + else contents = Array.prototype.slice.call(contents, position, position + length); + HEAP8.set(contents, ptr); + } + } + return { + ptr, + allocated + }; + }, + msync(stream, buffer, offset, length, mmapFlags) { + MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); + return 0; + } + } + }; + var FS_modeStringToFlags = (str) => { + var flags = { + "r": 0, + "r+": 2, + "w": 577, + "w+": 578, + "a": 1089, + "a+": 1090 + }[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 |= 365; + if (canWrite) mode |= 146; + return mode; + }; + var asyncLoad = async (url) => { + var arrayBuffer = await readAsync(url); + return new Uint8Array(arrayBuffer); + }; + var FS_createDataFile = (...args) => FS.createDataFile(...args); + var getUniqueRunDependency = (id) => { + return id; + }; + var runDependencies = 0; + var dependenciesFulfilled = null; + var removeRunDependency = (id) => { + runDependencies--; + Module["monitorRunDependencies"]?.(runDependencies); + if (runDependencies == 0) { + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); + } + } + }; + var addRunDependency = (id) => { + runDependencies++; + Module["monitorRunDependencies"]?.(runDependencies); + }; + var preloadPlugins = []; + var FS_handledByPreloadPlugin = async (byteArray, fullname) => { + if (typeof Browser != "undefined") Browser.init(); + for (var plugin of preloadPlugins) if (plugin["canHandle"](fullname)) return plugin["handle"](byteArray, fullname); + return byteArray; + }; + var FS_preloadFile = async (parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish) => { + var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; + var dep = getUniqueRunDependency(`cp ${fullname}`); + addRunDependency(dep); + try { + var byteArray = url; + if (typeof url == "string") byteArray = await asyncLoad(url); + byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); + preFinish?.(); + if (!dontCreateFile) FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); + } finally { + removeRunDependency(dep); + } + }; + var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { + FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror); + }; + var FS = { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: "/", + initialized: false, + ignorePermissions: true, + filesystems: null, + syncFSRequests: 0, + readFiles: {}, + ErrnoError: class { + name = "ErrnoError"; + constructor(errno) { + this.errno = errno; + } + }, + FSStream: class { + 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 { + node_ops = {}; + stream_ops = {}; + readMode = 365; + writeMode = 146; + mounted = null; + constructor(parent, name, mode, rdev) { + if (!parent) parent = this; + this.parent = parent; + this.mount = parent.mount; + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.rdev = rdev; + this.atime = this.mtime = this.ctime = Date.now(); + } + 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 = {}) { + if (!path) throw new FS.ErrnoError(44); + opts.follow_mount ??= true; + if (!PATH.isAbs(path)) path = FS.cwd() + "/" + path; + linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { + var parts = path.split("/").filter((p) => !!p); + var current = FS.root; + var current_path = "/"; + for (var i = 0; i < parts.length; i++) { + var islast = i === parts.length - 1; + if (islast && opts.parent) break; + if (parts[i] === ".") continue; + if (parts[i] === "..") { + current_path = PATH.dirname(current_path); + if (FS.isRoot(current)) { + path = current_path + "/" + parts.slice(i + 1).join("/"); + nlinks--; + continue linkloop; + } else current = current.parent; + continue; + } + current_path = PATH.join2(current_path, parts[i]); + try { + current = FS.lookupNode(current, parts[i]); + } catch (e) { + if (e?.errno === 44 && islast && opts.noent_okay) return { path: current_path }; + throw e; + } + if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) current = current.mounted.root; + if (FS.isLink(current.mode) && (!islast || opts.follow)) { + if (!current.node_ops.readlink) throw new FS.ErrnoError(52); + var link = current.node_ops.readlink(current); + if (!PATH.isAbs(link)) link = PATH.dirname(current_path) + "/" + link; + path = link + "/" + parts.slice(i + 1).join("/"); + continue linkloop; + } + } + return { + path: current_path, + node: current + }; + } + throw new FS.ErrnoError(32); + }, + 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 < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0; + return (parentid + hash >>> 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) { + if (!FS.isDir(dir.mode)) return 54; + try { + 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 & 576) return 31; + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + }, + checkOpExists(op, err) { + if (!op) throw new FS.ErrnoError(err); + return op; + }, + 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; + }, + doSetAttr(stream, node, attr) { + var setattr = stream?.stream_ops.setattr; + var arg = setattr ? stream : node; + setattr ??= node.node_ops.setattr; + FS.checkOpExists(setattr, 63); + setattr(arg, attr); + }, + chrdev_stream_ops: { + open(stream) { + stream.stream_ops = FS.getDevice(stream.node.rdev).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); + } + for (var mount of mounts) if (mount.type.syncfs) mount.type.syncfs(mount, populate, done); + else done(null); + }, + 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); + for (var [hash, current] of Object.entries(FS.nameTable)) 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 parent = FS.lookupPath(path, { parent: true }).node; + var name = PATH.basename(path); + if (!name) throw new FS.ErrnoError(28); + if (name === "." || name === "..") throw new FS.ErrnoError(20); + 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); + }, + statfs(path) { + return FS.statfsNode(FS.lookupPath(path, { follow: true }).node); + }, + statfsStream(stream) { + return FS.statfsNode(stream.node); + }, + statfsNode(node) { + var rtn = { + bsize: 4096, + frsize: 4096, + blocks: 1e6, + bfree: 5e5, + bavail: 5e5, + files: FS.nextInode, + ffree: FS.nextInode - 1, + fsid: 42, + flags: 2, + namelen: 255 + }; + if (node.node_ops.statfs) Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root)); + return rtn; + }, + create(path, mode = 438) { + mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + }, + mkdir(path, mode = 511) { + mode &= 1023; + mode |= 16384; + return FS.mknod(path, mode, 0); + }, + mkdirTree(path, mode) { + var dirs = path.split("/"); + var d = ""; + for (var dir of dirs) { + if (!dir) continue; + if (d || PATH.isAbs(path)) d += "/"; + d += dir; + try { + FS.mkdir(d, mode); + } catch (e) { + if (e.errno != 20) throw e; + } + } + }, + mkdev(path, mode, dev) { + if (typeof dev == "undefined") { + dev = mode; + mode = 438; + } + mode |= 8192; + return FS.mknod(path, mode, dev); + }, + symlink(oldpath, newpath) { + if (!PATH_FS.resolve(oldpath)) throw new FS.ErrnoError(44); + var parent = FS.lookupPath(newpath, { parent: true }).node; + if (!parent) throw new FS.ErrnoError(44); + var newname = PATH.basename(newpath); + var errCode = FS.mayCreate(parent, newname); + if (errCode) throw new FS.ErrnoError(errCode); + if (!parent.node_ops.symlink) throw new FS.ErrnoError(63); + return parent.node_ops.symlink(parent, newname, oldpath); + }, + rename(old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + var lookup = FS.lookupPath(old_path, { parent: true }), old_dir = lookup.node, new_dir; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + if (!old_dir || !new_dir) throw new FS.ErrnoError(44); + if (old_dir.mount !== new_dir.mount) throw new FS.ErrnoError(75); + var old_node = FS.lookupNode(old_dir, old_name); + var relative = PATH_FS.relative(old_path, new_dirname); + if (relative.charAt(0) !== ".") throw new FS.ErrnoError(28); + relative = PATH_FS.relative(new_path, old_dirname); + if (relative.charAt(0) !== ".") throw new FS.ErrnoError(55); + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (old_node === new_node) return; + var isdir = FS.isDir(old_node.mode); + var errCode = FS.mayDelete(old_dir, old_name, isdir); + if (errCode) throw new FS.ErrnoError(errCode); + errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); + if (errCode) throw new FS.ErrnoError(errCode); + if (!old_dir.node_ops.rename) throw new FS.ErrnoError(63); + if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) throw new FS.ErrnoError(10); + if (new_dir !== old_dir) { + errCode = FS.nodePermissions(old_dir, "w"); + if (errCode) throw new FS.ErrnoError(errCode); + } + FS.hashRemoveNode(old_node); + try { + old_dir.node_ops.rename(old_node, new_dir, new_name); + old_node.parent = new_dir; + } catch (e) { + throw e; + } finally { + FS.hashAddNode(old_node); + } + }, + rmdir(path) { + var parent = FS.lookupPath(path, { parent: true }).node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, true); + if (errCode) throw new FS.ErrnoError(errCode); + if (!parent.node_ops.rmdir) throw new FS.ErrnoError(63); + if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + }, + readdir(path) { + var node = FS.lookupPath(path, { follow: true }).node; + return FS.checkOpExists(node.node_ops.readdir, 54)(node); + }, + unlink(path) { + var parent = FS.lookupPath(path, { parent: true }).node; + if (!parent) throw new FS.ErrnoError(44); + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, false); + if (errCode) throw new FS.ErrnoError(errCode); + if (!parent.node_ops.unlink) throw new FS.ErrnoError(63); + if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + }, + readlink(path) { + var link = FS.lookupPath(path).node; + if (!link) throw new FS.ErrnoError(44); + if (!link.node_ops.readlink) throw new FS.ErrnoError(28); + return link.node_ops.readlink(link); + }, + stat(path, dontFollow) { + var node = FS.lookupPath(path, { follow: !dontFollow }).node; + return FS.checkOpExists(node.node_ops.getattr, 63)(node); + }, + fstat(fd) { + var stream = FS.getStreamChecked(fd); + var node = stream.node; + var getattr = stream.stream_ops.getattr; + var arg = getattr ? stream : node; + getattr ??= node.node_ops.getattr; + FS.checkOpExists(getattr, 63); + return getattr(arg); + }, + lstat(path) { + return FS.stat(path, true); + }, + doChmod(stream, node, mode, dontFollow) { + FS.doSetAttr(stream, node, { + mode: mode & 4095 | node.mode & -4096, + ctime: Date.now(), + dontFollow + }); + }, + chmod(path, mode, dontFollow) { + var node; + if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; + else node = path; + FS.doChmod(null, node, mode, dontFollow); + }, + lchmod(path, mode) { + FS.chmod(path, mode, true); + }, + fchmod(fd, mode) { + var stream = FS.getStreamChecked(fd); + FS.doChmod(stream, stream.node, mode, false); + }, + doChown(stream, node, dontFollow) { + FS.doSetAttr(stream, node, { + timestamp: Date.now(), + dontFollow + }); + }, + chown(path, uid, gid, dontFollow) { + var node; + if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; + else node = path; + FS.doChown(null, node, dontFollow); + }, + lchown(path, uid, gid) { + FS.chown(path, uid, gid, true); + }, + fchown(fd, uid, gid) { + var stream = FS.getStreamChecked(fd); + FS.doChown(stream, stream.node, false); + }, + doTruncate(stream, node, len) { + if (FS.isDir(node.mode)) throw new FS.ErrnoError(31); + if (!FS.isFile(node.mode)) throw new FS.ErrnoError(28); + var errCode = FS.nodePermissions(node, "w"); + if (errCode) throw new FS.ErrnoError(errCode); + FS.doSetAttr(stream, node, { + size: len, + timestamp: Date.now() + }); + }, + truncate(path, len) { + if (len < 0) throw new FS.ErrnoError(28); + var node; + if (typeof path == "string") node = FS.lookupPath(path, { follow: true }).node; + else node = path; + FS.doTruncate(null, node, len); + }, + ftruncate(fd, len) { + var stream = FS.getStreamChecked(fd); + if (len < 0 || (stream.flags & 2097155) === 0) throw new FS.ErrnoError(28); + FS.doTruncate(stream, stream.node, len); + }, + utime(path, atime, mtime) { + var node = FS.lookupPath(path, { follow: true }).node; + FS.checkOpExists(node.node_ops.setattr, 63)(node, { + atime, + mtime + }); + }, + open(path, flags, mode = 438) { + if (path === "") throw new FS.ErrnoError(44); + flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; + if (flags & 64) mode = mode & 4095 | 32768; + else mode = 0; + var node; + var isDirPath; + if (typeof path == "object") node = path; + else { + isDirPath = path.endsWith("/"); + var lookup = FS.lookupPath(path, { + follow: !(flags & 131072), + noent_okay: true + }); + node = lookup.node; + path = lookup.path; + } + var created = false; + if (flags & 64) if (node) { + if (flags & 128) throw new FS.ErrnoError(20); + } else if (isDirPath) throw new FS.ErrnoError(31); + else { + node = FS.mknod(path, mode | 511, 0); + created = true; + } + if (!node) throw new FS.ErrnoError(44); + if (FS.isChrdev(node.mode)) flags &= -513; + if (flags & 65536 && !FS.isDir(node.mode)) throw new FS.ErrnoError(54); + if (!created) { + var errCode = FS.mayOpen(node, flags); + if (errCode) throw new FS.ErrnoError(errCode); + } + if (flags & 512 && !created) FS.truncate(node, 0); + flags &= -131713; + var stream = FS.createStream({ + node, + path: FS.getPath(node), + flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + ungotten: [], + error: false + }); + if (stream.stream_ops.open) stream.stream_ops.open(stream); + if (created) FS.chmod(node, mode & 511); + if (Module["logReadFiles"] && !(flags & 1)) { + if (!(path in FS.readFiles)) FS.readFiles[path] = 1; + } + return stream; + }, + close(stream) { + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if (stream.getdents) stream.getdents = null; + try { + if (stream.stream_ops.close) stream.stream_ops.close(stream); + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + stream.fd = null; + }, + isClosed(stream) { + return stream.fd === null; + }, + llseek(stream, offset, whence) { + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if (!stream.seekable || !stream.stream_ops.llseek) throw new FS.ErrnoError(70); + if (whence != 0 && whence != 1 && whence != 2) throw new FS.ErrnoError(28); + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; + }, + read(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) throw new FS.ErrnoError(28); + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(8); + if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); + if (!stream.stream_ops.read) throw new FS.ErrnoError(28); + var seeking = typeof position != "undefined"; + if (!seeking) position = stream.position; + else if (!stream.seekable) throw new FS.ErrnoError(70); + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write(stream, buffer, offset, length, position, canOwn) { + if (length < 0 || position < 0) throw new FS.ErrnoError(28); + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if ((stream.flags & 2097155) === 0) throw new FS.ErrnoError(8); + if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); + if (!stream.stream_ops.write) throw new FS.ErrnoError(28); + if (stream.seekable && stream.flags & 1024) FS.llseek(stream, 0, 2); + var seeking = typeof position != "undefined"; + if (!seeking) position = stream.position; + else if (!stream.seekable) throw new FS.ErrnoError(70); + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); + if (!seeking) stream.position += bytesWritten; + return bytesWritten; + }, + mmap(stream, length, position, prot, flags) { + if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) throw new FS.ErrnoError(2); + if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(2); + if (!stream.stream_ops.mmap) throw new FS.ErrnoError(43); + if (!length) throw new FS.ErrnoError(28); + return stream.stream_ops.mmap(stream, length, position, prot, flags); + }, + msync(stream, buffer, offset, length, mmapFlags) { + if (!stream.stream_ops.msync) return 0; + return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); + }, + ioctl(stream, cmd, arg) { + if (!stream.stream_ops.ioctl) throw new FS.ErrnoError(59); + return stream.stream_ops.ioctl(stream, cmd, arg); + }, + readFile(path, opts = {}) { + opts.flags = opts.flags || 0; + opts.encoding = opts.encoding || "binary"; + if (opts.encoding !== "utf8" && opts.encoding !== "binary") abort(`Invalid encoding type "${opts.encoding}"`); + var stream = FS.open(path, opts.flags); + var length = FS.stat(path).size; + var buf = new Uint8Array(length); + FS.read(stream, buf, 0, length, 0); + if (opts.encoding === "utf8") buf = UTF8ArrayToString(buf); + FS.close(stream); + return buf; + }, + writeFile(path, data, opts = {}) { + opts.flags = opts.flags || 577; + var stream = FS.open(path, opts.flags, opts.mode); + if (typeof data == "string") data = new Uint8Array(intArrayFromString(data, true)); + if (ArrayBuffer.isView(data)) FS.write(stream, data, 0, data.byteLength, void 0, opts.canOwn); + else abort("Unsupported data type"); + FS.close(stream); + }, + cwd: () => FS.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, + llseek: () => 0 + }); + 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) { + randomFill(randomBuffer); + randomLeft = 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", 16895, 73); + node.stream_ops = { llseek: MEMFS.stream_ops.llseek }; + 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 }, + id: fd + 1 + }; + ret.parent = ret; + return ret; + }, + readdir() { + return Array.from(FS.streams.entries()).filter(([k, v]) => v).map(([k, v]) => k.toString()); + } + }; + 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"); + FS.open("/dev/stdin", 0); + FS.open("/dev/stdout", 1); + FS.open("/dev/stderr", 1); + }, + staticInit() { + FS.nameTable = new Array(4096); + FS.mount(MEMFS, {}, "/"); + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + FS.filesystems = { "MEMFS": 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 stream of FS.streams) if (stream) FS.close(stream); + }, + findObject(path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (!ret.exists) return null; + return ret.object; + }, + analyzePath(path, dontResolveLastLink) { + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) {} + var ret = { + isRoot: false, + exists: false, + error: 0, + name: null, + path: null, + object: null, + parentExists: false, + parentPath: null, + parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === "/"; + } catch (e) { + ret.error = e.errno; + } + return ret; + }, + createPath(parent, path, canRead, canWrite) { + parent = typeof parent == "string" ? parent : FS.getPath(parent); + var parts = path.split("/").reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join2(parent, part); + try { + FS.mkdir(current); + } catch (e) { + if (e.errno != 20) throw e; + } + parent = current; + } + return current; + }, + createFile(parent, name, properties, canRead, canWrite) { + var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); + var mode = FS_getMode(canRead, canWrite); + return FS.create(path, mode); + }, + createDataFile(parent, name, data, canRead, canWrite, canOwn) { + var path = name; + if (parent) { + parent = typeof parent == "string" ? parent : FS.getPath(parent); + path = name ? PATH.join2(parent, name) : parent; + } + var mode = FS_getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data == "string") { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + FS.chmod(node, mode | 146); + var stream = FS.open(node, 577); + FS.write(stream, data, 0, data.length, 0, canOwn); + FS.close(stream); + FS.chmod(node, mode); + } + }, + createDevice(parent, name, input, output) { + var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); + var mode = FS_getMode(!!input, !!output); + FS.createDevice.major ??= 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + FS.registerDevice(dev, { + open(stream) { + stream.seekable = false; + }, + close(stream) { + if (output?.buffer?.length) output(10); + }, + read(stream, buffer, offset, length, pos) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); + if (result === null || result === void 0) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) stream.node.atime = Date.now(); + return bytesRead; + }, + write(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) try { + output(buffer[offset + i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) stream.node.mtime = stream.node.ctime = Date.now(); + return i; + } + }); + return FS.mkdev(path, mode, dev); + }, + forceLoadFile(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + if (globalThis.XMLHttpRequest) abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + else try { + obj.contents = readBinary(obj.url); + } catch (e) { + throw new FS.ErrnoError(29); + } + }, + createLazyFile(parent, name, url, canRead, canWrite) { + class LazyUint8Array { + lengthKnown = false; + chunks = []; + get(idx) { + if (idx > this.length - 1 || idx < 0) return; + 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)) abort("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) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength - 1) abort("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)) abort("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== void 0) 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") abort("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 (globalThis.XMLHttpRequest) { + if (!ENVIRONMENT_IS_WORKER) abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"); + var properties = { + isDevice: false, + contents: new LazyUint8Array() + }; + } 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 = {}; + for (const [key, fn] of Object.entries(node.stream_ops)) 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 < size; i++) buffer[offset + i] = contents[position + i]; + else for (var i = 0; i < size; i++) buffer[offset + i] = contents.get(position + i); + return size; + } + stream_ops.read = (stream, buffer, offset, length, position) => { + 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; + } + }; + /** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index. + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { + if (!ptr) return ""; + var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); + return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); + }; + var SYSCALLS = { + calculateAt(dirfd, path, allowEmpty) { + if (PATH.isAbs(path)) return path; + var dir; + if (dirfd === -100) dir = FS.cwd(); + else dir = SYSCALLS.getStreamFromFD(dirfd).path; + if (path.length == 0) { + if (!allowEmpty) throw new FS.ErrnoError(44); + return dir; + } + return dir + "/" + path; + }, + writeStat(buf, stat) { + HEAPU32[buf >> 2] = stat.dev; + HEAPU32[buf + 4 >> 2] = stat.mode; + HEAPU32[buf + 8 >> 2] = stat.nlink; + HEAPU32[buf + 12 >> 2] = stat.uid; + HEAPU32[buf + 16 >> 2] = stat.gid; + HEAPU32[buf + 20 >> 2] = stat.rdev; + HEAP64[buf + 24 >> 3] = BigInt(stat.size); + 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(); + HEAP64[buf + 40 >> 3] = BigInt(Math.floor(atime / 1e3)); + HEAPU32[buf + 48 >> 2] = atime % 1e3 * 1e3 * 1e3; + HEAP64[buf + 56 >> 3] = BigInt(Math.floor(mtime / 1e3)); + HEAPU32[buf + 64 >> 2] = mtime % 1e3 * 1e3 * 1e3; + HEAP64[buf + 72 >> 3] = BigInt(Math.floor(ctime / 1e3)); + HEAPU32[buf + 80 >> 2] = ctime % 1e3 * 1e3 * 1e3; + HEAP64[buf + 88 >> 3] = BigInt(stat.ino); + return 0; + }, + writeStatFs(buf, stats) { + HEAPU32[buf + 4 >> 2] = stats.bsize; + HEAPU32[buf + 60 >> 2] = stats.bsize; + HEAP64[buf + 8 >> 3] = BigInt(stats.blocks); + HEAP64[buf + 16 >> 3] = BigInt(stats.bfree); + HEAP64[buf + 24 >> 3] = BigInt(stats.bavail); + HEAP64[buf + 32 >> 3] = BigInt(stats.files); + HEAP64[buf + 40 >> 3] = BigInt(stats.ffree); + HEAPU32[buf + 48 >> 2] = stats.fsid; + HEAPU32[buf + 64 >> 2] = stats.flags; + HEAPU32[buf + 56 >> 2] = stats.namelen; + }, + 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) { + return FS.getStreamChecked(fd); + }, + varargs: void 0, + getStr(ptr) { + return UTF8ToString(ptr); + } + }; + 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 & -8) return -28; + var node = FS.lookupPath(path, { follow: true }).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; + } + } + var 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++; + return FS.dupStream(stream, arg).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 { + return SYSCALLS.writeStat(buf, FS.fstat(fd)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + var INT53_MAX = 9007199254740992; + var INT53_MIN = -9007199254740992; + var bigintToI53Checked = (num) => num < INT53_MIN || num > INT53_MAX ? NaN : Number(num); + function ___syscall_ftruncate64(fd, length) { + length = bigintToI53Checked(length); + 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) => { + return 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 (size < cwdLengthInBytes) return -68; + stringToUTF8(cwd, buf, size); + return cwdLengthInBytes; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_ioctl(fd, op, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (op) { + case 21509: + if (!stream.tty) return -59; + return 0; + case 21505: + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcgets) { + var termios = stream.tty.ops.ioctl_tcgets(stream); + var argp = syscallGetVarargP(); + HEAP32[argp >> 2] = termios.c_iflag || 0; + HEAP32[argp + 4 >> 2] = termios.c_oflag || 0; + HEAP32[argp + 8 >> 2] = termios.c_cflag || 0; + HEAP32[argp + 12 >> 2] = termios.c_lflag || 0; + for (var i = 0; i < 32; i++) HEAP8[argp + i + 17] = termios.c_cc[i] || 0; + return 0; + } + return 0; + case 21510: + case 21511: + case 21512: + if (!stream.tty) return -59; + return 0; + case 21506: + case 21507: + case 21508: + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcsets) { + var argp = syscallGetVarargP(); + var c_iflag = HEAP32[argp >> 2]; + var c_oflag = HEAP32[argp + 4 >> 2]; + var c_cflag = HEAP32[argp + 8 >> 2]; + var c_lflag = HEAP32[argp + 12 >> 2]; + var c_cc = []; + for (var i = 0; i < 32; i++) c_cc.push(HEAP8[argp + i + 17]); + return stream.tty.ops.ioctl_tcsets(stream.tty, op, { + c_iflag, + c_oflag, + c_cflag, + c_lflag, + c_cc + }); + } + return 0; + case 21519: + if (!stream.tty) return -59; + var argp = syscallGetVarargP(); + HEAP32[argp >> 2] = 0; + return 0; + case 21520: + if (!stream.tty) return -59; + return -28; + case 21537: + case 21531: + var argp = syscallGetVarargP(); + return FS.ioctl(stream, op, argp); + case 21523: + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tiocgwinsz) { + var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); + var argp = syscallGetVarargP(); + HEAP16[argp >> 1] = winsize[0]; + HEAP16[argp + 2 >> 1] = winsize[1]; + } + return 0; + case 21524: + if (!stream.tty) return -59; + return 0; + case 21515: + if (!stream.tty) return -59; + return 0; + default: return -28; + } + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_lstat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.writeStat(buf, FS.lstat(path)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_mkdirat(dirfd, path, mode) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + FS.mkdir(path, mode, 0); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_newfstatat(dirfd, path, buf, flags) { + try { + path = SYSCALLS.getStr(path); + var nofollow = flags & 256; + var allowEmpty = flags & 4096; + flags = flags & -6401; + path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); + return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_openat(dirfd, path, flags, varargs) { + SYSCALLS.varargs = varargs; + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + var mode = varargs ? syscallGetVarargI() : 0; + return FS.open(path, flags, mode).fd; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_readlinkat(dirfd, path, buf, bufsize) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (bufsize <= 0) return -28; + var ret = FS.readlink(path); + var len = Math.min(bufsize, lengthBytesUTF8(ret)); + var endChar = HEAP8[buf + len]; + stringToUTF8(ret, buf, bufsize + 1); + HEAP8[buf + len] = endChar; + return len; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_rmdir(path) { + try { + path = SYSCALLS.getStr(path); + FS.rmdir(path); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_stat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.writeStat(buf, FS.stat(path)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_unlinkat(dirfd, path, flags) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (!flags) FS.unlink(path); + else if (flags === 512) FS.rmdir(path); + else return -28; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + var readI53FromI64 = (ptr) => { + return HEAPU32[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 = null; + 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 = null; + else mtime = seconds * 1e3 + nanoseconds / (1e3 * 1e3); + } + if ((mtime ?? atime) !== null) FS.utime(path, atime, mtime); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + 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) => { + return (isLeapYear(date.getFullYear()) ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE)[date.getMonth()] + date.getDate() - 1; + }; + function __localtime_js(time, tmPtr) { + time = bigintToI53Checked(time); + var date = /* @__PURE__ */ 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, allocated, addr) { + offset = bigintToI53Checked(offset); + try { + 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) { + offset = bigintToI53Checked(offset); + 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 __tzset_js = (timezone, daylight, std_name, dst_name) => { + var currentYear = (/* @__PURE__ */ 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); + return `UTC${sign}${String(Math.floor(absOffset / 60)).padStart(2, "0")}${String(absOffset % 60).padStart(2, "0")}`; + }; + var winterName = extractZone(winterOffset); + var summerName = extractZone(summerOffset); + if (summerOffset < winterOffset) { + stringToUTF8(winterName, std_name, 17); + stringToUTF8(summerName, dst_name, 17); + } else { + stringToUTF8(winterName, dst_name, 17); + stringToUTF8(summerName, std_name, 17); + } + }; + var _emscripten_get_now = () => performance.now(); + var _emscripten_date_now = () => Date.now(); + var nowIsMonotonic = 1; + var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + function _clock_time_get(clk_id, ignored_precision, ptime) { + ignored_precision = bigintToI53Checked(ignored_precision); + if (!checkWasiClock(clk_id)) return 28; + var now; + if (clk_id === 0) now = _emscripten_date_now(); + else if (nowIsMonotonic) now = _emscripten_get_now(); + else return 52; + var nsec = Math.round(now * 1e3 * 1e3); + HEAP64[ptime >> 3] = BigInt(nsec); + return 0; + } + var getHeapMax = () => 2147483648; + var _emscripten_get_heap_max = () => getHeapMax(); + var growMemory = (size) => { + var pages = (size - wasmMemory.buffer.byteLength + 65535) / 65536 | 0; + 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); + if (growMemory(Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)))) return true; + } + return false; + }; + var ENV = {}; + var getExecutableName = () => thisProgram || "./this.program"; + var getEnvStrings = () => { + if (!getEnvStrings.strings) { + var lang = (globalThis.navigator?.language ?? "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] === void 0) 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 _environ_get = (__environ, environ_buf) => { + var bufSize = 0; + var envp = 0; + for (var string of getEnvStrings()) { + var ptr = environ_buf + bufSize; + HEAPU32[__environ + envp >> 2] = ptr; + bufSize += stringToUTF8(string, ptr, Infinity) + 1; + envp += 4; + } + return 0; + }; + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { + var strings = getEnvStrings(); + HEAPU32[penviron_count >> 2] = strings.length; + var bufSize = 0; + for (var string of strings) bufSize += lengthBytesUTF8(string) + 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; + HEAP64[pbuf + 8 >> 3] = BigInt(rightsBase); + HEAP64[pbuf + 16 >> 3] = BigInt(rightsInheriting); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + /** @param {number=} offset */ + var doReadv = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 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 < len) break; + if (typeof offset != "undefined") offset += curr; + } + return ret; + }; + function _fd_read(fd, iov, iovcnt, pnum) { + try { + var num = doReadv(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + function _fd_seek(fd, offset, whence, newOffset) { + offset = bigintToI53Checked(offset); + try { + if (isNaN(offset)) return 61; + var stream = SYSCALLS.getStreamFromFD(fd); + FS.llseek(stream, offset, whence); + HEAP64[newOffset >> 3] = BigInt(stream.position); + 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); + return stream.stream_ops?.fsync?.(stream); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + /** @param {number=} offset */ + var doWritev = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 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 < len) break; + if (typeof offset != "undefined") offset += curr; + } + return ret; + }; + function _fd_write(fd, iov, iovcnt, pnum) { + try { + var num = doWritev(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + FS.createPreloadedFile = FS_createPreloadedFile; + FS.preloadFile = FS_preloadFile; + FS.staticInit(); + initMemory(); + if (Module["noExitRuntime"]) Module["noExitRuntime"]; + if (Module["preloadPlugins"]) preloadPlugins = Module["preloadPlugins"]; + if (Module["print"]) out = Module["print"]; + if (Module["printErr"]) err = Module["printErr"]; + if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; + if (Module["arguments"]) Module["arguments"]; + if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; + if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; + while (Module["preInit"].length > 0) Module["preInit"].shift()(); + } + Module["wasmMemory"] = wasmMemory; + var _emscripten_builtin_memalign; + function assignWasmExports(wasmExports) { + Module["_sqlite3_status64"] = wasmExports["sqlite3_status64"]; + Module["_sqlite3_status"] = wasmExports["sqlite3_status"]; + Module["_sqlite3_db_status64"] = wasmExports["sqlite3_db_status64"]; + Module["_sqlite3_msize"] = wasmExports["sqlite3_msize"]; + Module["_sqlite3_db_status"] = wasmExports["sqlite3_db_status"]; + Module["_sqlite3_vfs_find"] = wasmExports["sqlite3_vfs_find"]; + Module["_sqlite3_initialize"] = wasmExports["sqlite3_initialize"]; + Module["_sqlite3_malloc"] = wasmExports["sqlite3_malloc"]; + Module["_sqlite3_free"] = wasmExports["sqlite3_free"]; + Module["_sqlite3_vfs_register"] = wasmExports["sqlite3_vfs_register"]; + Module["_sqlite3_vfs_unregister"] = wasmExports["sqlite3_vfs_unregister"]; + Module["_sqlite3_malloc64"] = wasmExports["sqlite3_malloc64"]; + Module["_sqlite3_realloc"] = wasmExports["sqlite3_realloc"]; + Module["_sqlite3_realloc64"] = wasmExports["sqlite3_realloc64"]; + Module["_sqlite3_value_text"] = wasmExports["sqlite3_value_text"]; + Module["_sqlite3_randomness"] = wasmExports["sqlite3_randomness"]; + Module["_sqlite3_stricmp"] = wasmExports["sqlite3_stricmp"]; + Module["_sqlite3_strnicmp"] = wasmExports["sqlite3_strnicmp"]; + Module["_sqlite3_uri_parameter"] = wasmExports["sqlite3_uri_parameter"]; + Module["_sqlite3_uri_boolean"] = wasmExports["sqlite3_uri_boolean"]; + Module["_sqlite3_serialize"] = wasmExports["sqlite3_serialize"]; + Module["_sqlite3_prepare_v2"] = wasmExports["sqlite3_prepare_v2"]; + Module["_sqlite3_step"] = wasmExports["sqlite3_step"]; + Module["_sqlite3_column_int64"] = wasmExports["sqlite3_column_int64"]; + Module["_sqlite3_reset"] = wasmExports["sqlite3_reset"]; + Module["_sqlite3_exec"] = wasmExports["sqlite3_exec"]; + Module["_sqlite3_column_int"] = wasmExports["sqlite3_column_int"]; + Module["_sqlite3_finalize"] = wasmExports["sqlite3_finalize"]; + Module["_sqlite3_file_control"] = wasmExports["sqlite3_file_control"]; + Module["_sqlite3_column_name"] = wasmExports["sqlite3_column_name"]; + Module["_sqlite3_column_text"] = wasmExports["sqlite3_column_text"]; + Module["_sqlite3_column_type"] = wasmExports["sqlite3_column_type"]; + Module["_sqlite3_errmsg"] = wasmExports["sqlite3_errmsg"]; + Module["_sqlite3_deserialize"] = wasmExports["sqlite3_deserialize"]; + Module["_sqlite3_clear_bindings"] = wasmExports["sqlite3_clear_bindings"]; + Module["_sqlite3_value_blob"] = wasmExports["sqlite3_value_blob"]; + Module["_sqlite3_value_bytes"] = wasmExports["sqlite3_value_bytes"]; + Module["_sqlite3_value_double"] = wasmExports["sqlite3_value_double"]; + Module["_sqlite3_value_int"] = wasmExports["sqlite3_value_int"]; + Module["_sqlite3_value_int64"] = wasmExports["sqlite3_value_int64"]; + Module["_sqlite3_value_subtype"] = wasmExports["sqlite3_value_subtype"]; + Module["_sqlite3_value_pointer"] = wasmExports["sqlite3_value_pointer"]; + Module["_sqlite3_value_type"] = wasmExports["sqlite3_value_type"]; + Module["_sqlite3_value_nochange"] = wasmExports["sqlite3_value_nochange"]; + Module["_sqlite3_value_frombind"] = wasmExports["sqlite3_value_frombind"]; + Module["_sqlite3_value_dup"] = wasmExports["sqlite3_value_dup"]; + Module["_sqlite3_value_free"] = wasmExports["sqlite3_value_free"]; + Module["_sqlite3_result_blob"] = wasmExports["sqlite3_result_blob"]; + Module["_sqlite3_result_error_toobig"] = wasmExports["sqlite3_result_error_toobig"]; + Module["_sqlite3_result_error_nomem"] = wasmExports["sqlite3_result_error_nomem"]; + Module["_sqlite3_result_double"] = wasmExports["sqlite3_result_double"]; + Module["_sqlite3_result_error"] = wasmExports["sqlite3_result_error"]; + Module["_sqlite3_result_int"] = wasmExports["sqlite3_result_int"]; + Module["_sqlite3_result_int64"] = wasmExports["sqlite3_result_int64"]; + Module["_sqlite3_result_null"] = wasmExports["sqlite3_result_null"]; + Module["_sqlite3_result_pointer"] = wasmExports["sqlite3_result_pointer"]; + Module["_sqlite3_result_subtype"] = wasmExports["sqlite3_result_subtype"]; + Module["_sqlite3_result_text"] = wasmExports["sqlite3_result_text"]; + Module["_sqlite3_result_zeroblob"] = wasmExports["sqlite3_result_zeroblob"]; + Module["_sqlite3_result_zeroblob64"] = wasmExports["sqlite3_result_zeroblob64"]; + Module["_sqlite3_result_error_code"] = wasmExports["sqlite3_result_error_code"]; + Module["_sqlite3_user_data"] = wasmExports["sqlite3_user_data"]; + Module["_sqlite3_context_db_handle"] = wasmExports["sqlite3_context_db_handle"]; + Module["_sqlite3_vtab_nochange"] = wasmExports["sqlite3_vtab_nochange"]; + Module["_sqlite3_vtab_in_first"] = wasmExports["sqlite3_vtab_in_first"]; + Module["_sqlite3_vtab_in_next"] = wasmExports["sqlite3_vtab_in_next"]; + Module["_sqlite3_aggregate_context"] = wasmExports["sqlite3_aggregate_context"]; + Module["_sqlite3_get_auxdata"] = wasmExports["sqlite3_get_auxdata"]; + Module["_sqlite3_set_auxdata"] = wasmExports["sqlite3_set_auxdata"]; + Module["_sqlite3_column_count"] = wasmExports["sqlite3_column_count"]; + Module["_sqlite3_data_count"] = wasmExports["sqlite3_data_count"]; + Module["_sqlite3_column_blob"] = wasmExports["sqlite3_column_blob"]; + Module["_sqlite3_column_bytes"] = wasmExports["sqlite3_column_bytes"]; + Module["_sqlite3_column_double"] = wasmExports["sqlite3_column_double"]; + Module["_sqlite3_column_value"] = wasmExports["sqlite3_column_value"]; + Module["_sqlite3_column_decltype"] = wasmExports["sqlite3_column_decltype"]; + Module["_sqlite3_column_database_name"] = wasmExports["sqlite3_column_database_name"]; + Module["_sqlite3_column_table_name"] = wasmExports["sqlite3_column_table_name"]; + Module["_sqlite3_column_origin_name"] = wasmExports["sqlite3_column_origin_name"]; + Module["_sqlite3_bind_blob"] = wasmExports["sqlite3_bind_blob"]; + Module["_sqlite3_bind_double"] = wasmExports["sqlite3_bind_double"]; + Module["_sqlite3_bind_int"] = wasmExports["sqlite3_bind_int"]; + Module["_sqlite3_bind_int64"] = wasmExports["sqlite3_bind_int64"]; + Module["_sqlite3_bind_null"] = wasmExports["sqlite3_bind_null"]; + Module["_sqlite3_bind_pointer"] = wasmExports["sqlite3_bind_pointer"]; + Module["_sqlite3_bind_text"] = wasmExports["sqlite3_bind_text"]; + Module["_sqlite3_bind_parameter_count"] = wasmExports["sqlite3_bind_parameter_count"]; + Module["_sqlite3_bind_parameter_name"] = wasmExports["sqlite3_bind_parameter_name"]; + Module["_sqlite3_bind_parameter_index"] = wasmExports["sqlite3_bind_parameter_index"]; + Module["_sqlite3_db_handle"] = wasmExports["sqlite3_db_handle"]; + Module["_sqlite3_stmt_readonly"] = wasmExports["sqlite3_stmt_readonly"]; + Module["_sqlite3_stmt_isexplain"] = wasmExports["sqlite3_stmt_isexplain"]; + Module["_sqlite3_stmt_explain"] = wasmExports["sqlite3_stmt_explain"]; + Module["_sqlite3_stmt_busy"] = wasmExports["sqlite3_stmt_busy"]; + Module["_sqlite3_next_stmt"] = wasmExports["sqlite3_next_stmt"]; + Module["_sqlite3_stmt_status"] = wasmExports["sqlite3_stmt_status"]; + Module["_sqlite3_sql"] = wasmExports["sqlite3_sql"]; + Module["_sqlite3_expanded_sql"] = wasmExports["sqlite3_expanded_sql"]; + Module["_sqlite3_preupdate_old"] = wasmExports["sqlite3_preupdate_old"]; + Module["_sqlite3_preupdate_count"] = wasmExports["sqlite3_preupdate_count"]; + Module["_sqlite3_preupdate_depth"] = wasmExports["sqlite3_preupdate_depth"]; + Module["_sqlite3_preupdate_blobwrite"] = wasmExports["sqlite3_preupdate_blobwrite"]; + Module["_sqlite3_preupdate_new"] = wasmExports["sqlite3_preupdate_new"]; + Module["_sqlite3_value_numeric_type"] = wasmExports["sqlite3_value_numeric_type"]; + Module["_sqlite3_set_authorizer"] = wasmExports["sqlite3_set_authorizer"]; + Module["_sqlite3_strglob"] = wasmExports["sqlite3_strglob"]; + Module["_sqlite3_strlike"] = wasmExports["sqlite3_strlike"]; + Module["_sqlite3_auto_extension"] = wasmExports["sqlite3_auto_extension"]; + Module["_sqlite3_cancel_auto_extension"] = wasmExports["sqlite3_cancel_auto_extension"]; + Module["_sqlite3_reset_auto_extension"] = wasmExports["sqlite3_reset_auto_extension"]; + Module["_sqlite3_prepare_v3"] = wasmExports["sqlite3_prepare_v3"]; + Module["_sqlite3_create_module"] = wasmExports["sqlite3_create_module"]; + Module["_sqlite3_create_module_v2"] = wasmExports["sqlite3_create_module_v2"]; + Module["_sqlite3_drop_modules"] = wasmExports["sqlite3_drop_modules"]; + Module["_sqlite3_declare_vtab"] = wasmExports["sqlite3_declare_vtab"]; + Module["_sqlite3_vtab_on_conflict"] = wasmExports["sqlite3_vtab_on_conflict"]; + Module["_sqlite3_vtab_collation"] = wasmExports["sqlite3_vtab_collation"]; + Module["_sqlite3_vtab_in"] = wasmExports["sqlite3_vtab_in"]; + Module["_sqlite3_vtab_rhs_value"] = wasmExports["sqlite3_vtab_rhs_value"]; + Module["_sqlite3_vtab_distinct"] = wasmExports["sqlite3_vtab_distinct"]; + Module["_sqlite3_keyword_name"] = wasmExports["sqlite3_keyword_name"]; + Module["_sqlite3_keyword_count"] = wasmExports["sqlite3_keyword_count"]; + Module["_sqlite3_keyword_check"] = wasmExports["sqlite3_keyword_check"]; + Module["_sqlite3_complete"] = wasmExports["sqlite3_complete"]; + Module["_sqlite3_libversion"] = wasmExports["sqlite3_libversion"]; + Module["_sqlite3_libversion_number"] = wasmExports["sqlite3_libversion_number"]; + Module["_sqlite3_shutdown"] = wasmExports["sqlite3_shutdown"]; + Module["_sqlite3_last_insert_rowid"] = wasmExports["sqlite3_last_insert_rowid"]; + Module["_sqlite3_set_last_insert_rowid"] = wasmExports["sqlite3_set_last_insert_rowid"]; + Module["_sqlite3_changes64"] = wasmExports["sqlite3_changes64"]; + Module["_sqlite3_changes"] = wasmExports["sqlite3_changes"]; + Module["_sqlite3_total_changes64"] = wasmExports["sqlite3_total_changes64"]; + Module["_sqlite3_total_changes"] = wasmExports["sqlite3_total_changes"]; + Module["_sqlite3_txn_state"] = wasmExports["sqlite3_txn_state"]; + Module["_sqlite3_close_v2"] = wasmExports["sqlite3_close_v2"]; + Module["_sqlite3_busy_handler"] = wasmExports["sqlite3_busy_handler"]; + Module["_sqlite3_progress_handler"] = wasmExports["sqlite3_progress_handler"]; + Module["_sqlite3_busy_timeout"] = wasmExports["sqlite3_busy_timeout"]; + Module["_sqlite3_interrupt"] = wasmExports["sqlite3_interrupt"]; + Module["_sqlite3_is_interrupted"] = wasmExports["sqlite3_is_interrupted"]; + Module["_sqlite3_create_function"] = wasmExports["sqlite3_create_function"]; + Module["_sqlite3_create_function_v2"] = wasmExports["sqlite3_create_function_v2"]; + Module["_sqlite3_create_window_function"] = wasmExports["sqlite3_create_window_function"]; + Module["_sqlite3_overload_function"] = wasmExports["sqlite3_overload_function"]; + Module["_sqlite3_trace_v2"] = wasmExports["sqlite3_trace_v2"]; + Module["_sqlite3_commit_hook"] = wasmExports["sqlite3_commit_hook"]; + Module["_sqlite3_update_hook"] = wasmExports["sqlite3_update_hook"]; + Module["_sqlite3_rollback_hook"] = wasmExports["sqlite3_rollback_hook"]; + Module["_sqlite3_preupdate_hook"] = wasmExports["sqlite3_preupdate_hook"]; + Module["_sqlite3_set_errmsg"] = wasmExports["sqlite3_set_errmsg"]; + Module["_sqlite3_error_offset"] = wasmExports["sqlite3_error_offset"]; + Module["_sqlite3_errcode"] = wasmExports["sqlite3_errcode"]; + Module["_sqlite3_extended_errcode"] = wasmExports["sqlite3_extended_errcode"]; + Module["_sqlite3_errstr"] = wasmExports["sqlite3_errstr"]; + Module["_sqlite3_limit"] = wasmExports["sqlite3_limit"]; + Module["_sqlite3_open"] = wasmExports["sqlite3_open"]; + Module["_sqlite3_open_v2"] = wasmExports["sqlite3_open_v2"]; + Module["_sqlite3_create_collation"] = wasmExports["sqlite3_create_collation"]; + Module["_sqlite3_create_collation_v2"] = wasmExports["sqlite3_create_collation_v2"]; + Module["_sqlite3_collation_needed"] = wasmExports["sqlite3_collation_needed"]; + Module["_sqlite3_get_autocommit"] = wasmExports["sqlite3_get_autocommit"]; + Module["_sqlite3_table_column_metadata"] = wasmExports["sqlite3_table_column_metadata"]; + Module["_sqlite3_extended_result_codes"] = wasmExports["sqlite3_extended_result_codes"]; + Module["_sqlite3_uri_key"] = wasmExports["sqlite3_uri_key"]; + Module["_sqlite3_uri_int64"] = wasmExports["sqlite3_uri_int64"]; + Module["_sqlite3_db_name"] = wasmExports["sqlite3_db_name"]; + Module["_sqlite3_db_filename"] = wasmExports["sqlite3_db_filename"]; + Module["_sqlite3_db_readonly"] = wasmExports["sqlite3_db_readonly"]; + Module["_sqlite3_compileoption_used"] = wasmExports["sqlite3_compileoption_used"]; + Module["_sqlite3_compileoption_get"] = wasmExports["sqlite3_compileoption_get"]; + Module["_sqlite3session_diff"] = wasmExports["sqlite3session_diff"]; + Module["_sqlite3session_attach"] = wasmExports["sqlite3session_attach"]; + Module["_sqlite3session_create"] = wasmExports["sqlite3session_create"]; + Module["_sqlite3session_delete"] = wasmExports["sqlite3session_delete"]; + Module["_sqlite3session_table_filter"] = wasmExports["sqlite3session_table_filter"]; + Module["_sqlite3session_changeset"] = wasmExports["sqlite3session_changeset"]; + Module["_sqlite3session_changeset_strm"] = wasmExports["sqlite3session_changeset_strm"]; + Module["_sqlite3session_patchset_strm"] = wasmExports["sqlite3session_patchset_strm"]; + Module["_sqlite3session_patchset"] = wasmExports["sqlite3session_patchset"]; + Module["_sqlite3session_enable"] = wasmExports["sqlite3session_enable"]; + Module["_sqlite3session_indirect"] = wasmExports["sqlite3session_indirect"]; + Module["_sqlite3session_isempty"] = wasmExports["sqlite3session_isempty"]; + Module["_sqlite3session_memory_used"] = wasmExports["sqlite3session_memory_used"]; + Module["_sqlite3session_object_config"] = wasmExports["sqlite3session_object_config"]; + Module["_sqlite3session_changeset_size"] = wasmExports["sqlite3session_changeset_size"]; + Module["_sqlite3changeset_start"] = wasmExports["sqlite3changeset_start"]; + Module["_sqlite3changeset_start_v2"] = wasmExports["sqlite3changeset_start_v2"]; + Module["_sqlite3changeset_start_strm"] = wasmExports["sqlite3changeset_start_strm"]; + Module["_sqlite3changeset_start_v2_strm"] = wasmExports["sqlite3changeset_start_v2_strm"]; + Module["_sqlite3changeset_next"] = wasmExports["sqlite3changeset_next"]; + Module["_sqlite3changeset_op"] = wasmExports["sqlite3changeset_op"]; + Module["_sqlite3changeset_pk"] = wasmExports["sqlite3changeset_pk"]; + Module["_sqlite3changeset_old"] = wasmExports["sqlite3changeset_old"]; + Module["_sqlite3changeset_new"] = wasmExports["sqlite3changeset_new"]; + Module["_sqlite3changeset_conflict"] = wasmExports["sqlite3changeset_conflict"]; + Module["_sqlite3changeset_fk_conflicts"] = wasmExports["sqlite3changeset_fk_conflicts"]; + Module["_sqlite3changeset_finalize"] = wasmExports["sqlite3changeset_finalize"]; + Module["_sqlite3changeset_invert"] = wasmExports["sqlite3changeset_invert"]; + Module["_sqlite3changeset_invert_strm"] = wasmExports["sqlite3changeset_invert_strm"]; + Module["_sqlite3changeset_apply_v2"] = wasmExports["sqlite3changeset_apply_v2"]; + Module["_sqlite3changeset_apply_v3"] = wasmExports["sqlite3changeset_apply_v3"]; + Module["_sqlite3changeset_apply"] = wasmExports["sqlite3changeset_apply"]; + Module["_sqlite3changeset_apply_v3_strm"] = wasmExports["sqlite3changeset_apply_v3_strm"]; + Module["_sqlite3changeset_apply_v2_strm"] = wasmExports["sqlite3changeset_apply_v2_strm"]; + Module["_sqlite3changeset_apply_strm"] = wasmExports["sqlite3changeset_apply_strm"]; + Module["_sqlite3changegroup_new"] = wasmExports["sqlite3changegroup_new"]; + Module["_sqlite3changegroup_add"] = wasmExports["sqlite3changegroup_add"]; + Module["_sqlite3changegroup_output"] = wasmExports["sqlite3changegroup_output"]; + Module["_sqlite3changegroup_add_strm"] = wasmExports["sqlite3changegroup_add_strm"]; + Module["_sqlite3changegroup_output_strm"] = wasmExports["sqlite3changegroup_output_strm"]; + Module["_sqlite3changegroup_delete"] = wasmExports["sqlite3changegroup_delete"]; + Module["_sqlite3changeset_concat"] = wasmExports["sqlite3changeset_concat"]; + Module["_sqlite3changeset_concat_strm"] = wasmExports["sqlite3changeset_concat_strm"]; + Module["_sqlite3session_config"] = wasmExports["sqlite3session_config"]; + Module["_sqlite3_sourceid"] = wasmExports["sqlite3_sourceid"]; + Module["_sqlite3__wasm_pstack_ptr"] = wasmExports["sqlite3__wasm_pstack_ptr"]; + Module["_sqlite3__wasm_pstack_restore"] = wasmExports["sqlite3__wasm_pstack_restore"]; + Module["_sqlite3__wasm_pstack_alloc"] = wasmExports["sqlite3__wasm_pstack_alloc"]; + Module["_sqlite3__wasm_pstack_remaining"] = wasmExports["sqlite3__wasm_pstack_remaining"]; + Module["_sqlite3__wasm_pstack_quota"] = wasmExports["sqlite3__wasm_pstack_quota"]; + Module["_sqlite3__wasm_test_struct"] = wasmExports["sqlite3__wasm_test_struct"]; + Module["_sqlite3__wasm_enum_json"] = wasmExports["sqlite3__wasm_enum_json"]; + Module["_sqlite3__wasm_vfs_unlink"] = wasmExports["sqlite3__wasm_vfs_unlink"]; + Module["_sqlite3__wasm_db_vfs"] = wasmExports["sqlite3__wasm_db_vfs"]; + Module["_sqlite3__wasm_db_reset"] = wasmExports["sqlite3__wasm_db_reset"]; + Module["_sqlite3__wasm_db_export_chunked"] = wasmExports["sqlite3__wasm_db_export_chunked"]; + Module["_sqlite3__wasm_db_serialize"] = wasmExports["sqlite3__wasm_db_serialize"]; + Module["_sqlite3__wasm_vfs_create_file"] = wasmExports["sqlite3__wasm_vfs_create_file"]; + Module["_sqlite3__wasm_posix_create_file"] = wasmExports["sqlite3__wasm_posix_create_file"]; + Module["_sqlite3__wasm_kvvfsMakeKey"] = wasmExports["sqlite3__wasm_kvvfsMakeKey"]; + Module["_sqlite3__wasm_kvvfs_methods"] = wasmExports["sqlite3__wasm_kvvfs_methods"]; + Module["_sqlite3__wasm_vtab_config"] = wasmExports["sqlite3__wasm_vtab_config"]; + Module["_sqlite3__wasm_db_config_ip"] = wasmExports["sqlite3__wasm_db_config_ip"]; + Module["_sqlite3__wasm_db_config_pii"] = wasmExports["sqlite3__wasm_db_config_pii"]; + Module["_sqlite3__wasm_db_config_s"] = wasmExports["sqlite3__wasm_db_config_s"]; + Module["_sqlite3__wasm_config_i"] = wasmExports["sqlite3__wasm_config_i"]; + Module["_sqlite3__wasm_config_ii"] = wasmExports["sqlite3__wasm_config_ii"]; + Module["_sqlite3__wasm_config_j"] = wasmExports["sqlite3__wasm_config_j"]; + Module["_sqlite3__wasm_qfmt_token"] = wasmExports["sqlite3__wasm_qfmt_token"]; + Module["_sqlite3__wasm_kvvfs_decode"] = wasmExports["sqlite3__wasm_kvvfs_decode"]; + Module["_sqlite3__wasm_kvvfs_encode"] = wasmExports["sqlite3__wasm_kvvfs_encode"]; + Module["_sqlite3__wasm_init_wasmfs"] = wasmExports["sqlite3__wasm_init_wasmfs"]; + Module["_sqlite3__wasm_test_intptr"] = wasmExports["sqlite3__wasm_test_intptr"]; + Module["_sqlite3__wasm_test_voidptr"] = wasmExports["sqlite3__wasm_test_voidptr"]; + Module["_sqlite3__wasm_test_int64_max"] = wasmExports["sqlite3__wasm_test_int64_max"]; + Module["_sqlite3__wasm_test_int64_min"] = wasmExports["sqlite3__wasm_test_int64_min"]; + Module["_sqlite3__wasm_test_int64_times2"] = wasmExports["sqlite3__wasm_test_int64_times2"]; + Module["_sqlite3__wasm_test_int64_minmax"] = wasmExports["sqlite3__wasm_test_int64_minmax"]; + Module["_sqlite3__wasm_test_int64ptr"] = wasmExports["sqlite3__wasm_test_int64ptr"]; + Module["_sqlite3__wasm_test_stack_overflow"] = wasmExports["sqlite3__wasm_test_stack_overflow"]; + Module["_sqlite3__wasm_test_str_hello"] = wasmExports["sqlite3__wasm_test_str_hello"]; + Module["_sqlite3__wasm_SQLTester_strglob"] = wasmExports["sqlite3__wasm_SQLTester_strglob"]; + Module["_malloc"] = wasmExports["malloc"]; + Module["_free"] = wasmExports["free"]; + Module["_realloc"] = wasmExports["realloc"]; + _emscripten_builtin_memalign = wasmExports["emscripten_builtin_memalign"]; + wasmExports["_emscripten_stack_restore"]; + wasmExports["_emscripten_stack_alloc"]; + wasmExports["emscripten_stack_get_current"]; + wasmExports["__indirect_function_table"]; + } + var wasmImports = { + __syscall_chmod: ___syscall_chmod, + __syscall_faccessat: ___syscall_faccessat, + __syscall_fchmod: ___syscall_fchmod, + __syscall_fchown32: ___syscall_fchown32, + __syscall_fcntl64: ___syscall_fcntl64, + __syscall_fstat64: ___syscall_fstat64, + __syscall_ftruncate64: ___syscall_ftruncate64, + __syscall_getcwd: ___syscall_getcwd, + __syscall_ioctl: ___syscall_ioctl, + __syscall_lstat64: ___syscall_lstat64, + __syscall_mkdirat: ___syscall_mkdirat, + __syscall_newfstatat: ___syscall_newfstatat, + __syscall_openat: ___syscall_openat, + __syscall_readlinkat: ___syscall_readlinkat, + __syscall_rmdir: ___syscall_rmdir, + __syscall_stat64: ___syscall_stat64, + __syscall_unlinkat: ___syscall_unlinkat, + __syscall_utimensat: ___syscall_utimensat, + _localtime_js: __localtime_js, + _mmap_js: __mmap_js, + _munmap_js: __munmap_js, + _tzset_js: __tzset_js, + clock_time_get: _clock_time_get, + emscripten_date_now: _emscripten_date_now, + emscripten_get_heap_max: _emscripten_get_heap_max, + emscripten_get_now: _emscripten_get_now, + emscripten_resize_heap: _emscripten_resize_heap, + environ_get: _environ_get, + environ_sizes_get: _environ_sizes_get, + fd_close: _fd_close, + fd_fdstat_get: _fd_fdstat_get, + fd_read: _fd_read, + fd_seek: _fd_seek, + fd_sync: _fd_sync, + fd_write: _fd_write, + memory: wasmMemory + }; + function run() { + if (runDependencies > 0) { + dependenciesFulfilled = run; + return; + } + preRun(); + if (runDependencies > 0) { + dependenciesFulfilled = run; + return; + } + function doRun() { + Module["calledRun"] = true; + if (ABORT) return; + initRuntime(); + readyPromiseResolve?.(Module); + Module["onRuntimeInitialized"]?.(); + postRun(); + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(() => { + setTimeout(() => Module["setStatus"](""), 1); + doRun(); + }, 1); + } else doRun(); + } + var wasmExports = await createWasm(); + run(); + /** + post-js-header.js is to be prepended to other code to create + post-js.js for use with Emscripten's --post-js flag, so it gets + injected in the earliest stages of sqlite3InitModule(). + + Running this function will bootstrap the library and return + a Promise to the sqlite3 namespace object. + + In the canonical builds, this gets called by extern-post-js.c-pp.js + */ + Module.runSQLite3PostLoadInit = async function(sqlite3InitScriptInfo, EmscriptenModule, sqlite3IsUnderTest) { + /** ^^^ Don't use Module.postRun, as that runs a different time + depending on whether this file is built with emcc 3.1.x or + 4.0.x. This function name is intentionally obnoxiously verbose to + ensure that we don't collide with current and future Emscripten + symbol names. */ + "use strict"; + delete EmscriptenModule.runSQLite3PostLoadInit; + globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(apiConfig = globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) { + if (sqlite3ApiBootstrap.sqlite3) { + (sqlite3ApiBootstrap.sqlite3.config || console).warn("sqlite3ApiBootstrap() called multiple times.", "Config and external initializers are ignored on calls after the first."); + return sqlite3ApiBootstrap.sqlite3; + } + const config = Object.assign(Object.create(null), { + exports: void 0, + memory: void 0, + bigIntEnabled: !!globalThis.BigInt64Array, + debug: console.debug.bind(console), + warn: console.warn.bind(console), + error: console.error.bind(console), + log: console.log.bind(console), + wasmfsOpfsDir: "/opfs", + useStdAlloc: false + }, apiConfig || {}); + Object.assign(config, { + allocExportName: config.useStdAlloc ? "malloc" : "sqlite3_malloc", + deallocExportName: config.useStdAlloc ? "free" : "sqlite3_free", + reallocExportName: config.useStdAlloc ? "realloc" : "sqlite3_realloc" + }); + [ + "exports", + "memory", + "functionTable", + "wasmfsOpfsDir" + ].forEach((k) => { + if ("function" === typeof config[k]) config[k] = config[k](); + }); + /** + The main sqlite3 binding API gets installed into this object, + mimicking the C API as closely as we can. The numerous members + names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as + possible, identically to the C-native counterparts, as documented at: + + https://sqlite.org/c3ref/intro.html + + A very few exceptions require an additional level of proxy + function or may otherwise require special attention in the WASM + environment, and all such cases are documented somewhere below + in this file or in sqlite3-api-glue.js. capi members which are + not documented are installed as 1-to-1 proxies for their + C-side counterparts. + */ + const capi = Object.create(null); + /** + Holds state which are specific to the WASM-related + infrastructure and glue code. + + Note that a number of members of this object are injected + dynamically after the api object is fully constructed, so + not all are documented in this file. + */ + const wasm = Object.create(null); + /** Internal helper for SQLite3Error ctor. */ + const __rcStr = (rc) => { + return capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc) || "Unknown result code #" + rc; + }; + /** Internal helper for SQLite3Error ctor. */ + const isInt32 = (n) => "number" === typeof n && n === (n | 0) && n <= 2147483647 && n >= -2147483648; + /** + An Error subclass specifically for reporting DB-level errors and + enabling clients to unambiguously identify such exceptions. + The C-level APIs never throw, but some of the higher-level + C-style APIs do and the object-oriented APIs use exceptions + exclusively to report errors. + */ + class SQLite3Error extends Error { + /** + Constructs this object with a message depending on its arguments: + + If its first argument is an integer, it is assumed to be + an SQLITE_... result code and it is passed to + sqlite3.capi.sqlite3_js_rc_str() to stringify it. + + If called with exactly 2 arguments and the 2nd is an object, + that object is treated as the 2nd argument to the parent + constructor. + + The exception's message is created by concatenating its + arguments with a space between each, except for the + two-args-with-an-object form and that the first argument will + get coerced to a string, as described above, if it's an + integer. + + If passed an integer first argument, the error object's + `resultCode` member will be set to the given integer value, + else it will be set to capi.SQLITE_ERROR. + */ + constructor(...args) { + let rc; + if (args.length) if (isInt32(args[0])) { + rc = args[0]; + if (1 === args.length) super(__rcStr(args[0])); + else { + const rcStr = __rcStr(rc); + if ("object" === typeof args[1]) super(rcStr, args[1]); + else { + args[0] = rcStr + ":"; + super(args.join(" ")); + } + } + } else if (2 === args.length && "object" === typeof args[1]) super(...args); + else super(args.join(" ")); + this.resultCode = rc || capi.SQLITE_ERROR; + this.name = "SQLite3Error"; + } + } + /** + Functionally equivalent to the SQLite3Error constructor but may + be used as part of an expression, e.g.: + + ``` + return someFunction(x) || SQLite3Error.toss(...); + ``` + */ + SQLite3Error.toss = (...args) => { + throw new SQLite3Error(...args); + }; + const toss3 = SQLite3Error.toss; + if (config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)) toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'."); + /** + Returns true if the given BigInt value is small enough to fit + into an int64 value, else false. + */ + const bigIntFits64 = function f(b) { + if (!f._max) { + f._max = BigInt("0x7fffffffffffffff"); + f._min = ~f._max; + } + return b >= f._min && b <= f._max; + }; + /** + Returns true if the given BigInt value is small enough to fit + into an int32, else false. + */ + const bigIntFits32 = (b) => b >= -2147483647n - 1n && b <= 2147483647n; + /** + Returns true if the given BigInt value is small enough to fit + into a double value without loss of precision, else false. + */ + const bigIntFitsDouble = function f(b) { + if (!f._min) { + f._min = Number.MIN_SAFE_INTEGER; + f._max = Number.MAX_SAFE_INTEGER; + } + return b >= f._min && b <= f._max; + }; + /** Returns v if v appears to be a TypedArray, else false. */ + const isTypedArray = (v) => { + return v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT) ? v : false; + }; + /** + Returns true if v appears to be one of our bind()-able TypedArray + types: Uint8Array or Int8Array or ArrayBuffer. Support for + TypedArrays with element sizes >1 is a potential TODO just + waiting on a use case to justify them. Until then, their `buffer` + property can be used to pass them as an ArrayBuffer. If it's not + a bindable array type, a falsy value is returned. + */ + const isBindableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); + /** + Returns true if v appears to be one of the TypedArray types + which is legal for holding SQL code (as opposed to binary blobs). + + Currently this is the same as isBindableTypedArray() but it + seems likely that we'll eventually want to add Uint32Array + and friends to the isBindableTypedArray() list but not to the + isSQLableTypedArray() list. + */ + const isSQLableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); + /** Returns true if isBindableTypedArray(v) does, else throws with a message + that v is not a supported TypedArray value. */ + const affirmBindableTypedArray = (v) => isBindableTypedArray(v) || toss3("Value is not of a supported TypedArray type."); + /** + If v is-a Array, its join("") result is returned. If + isSQLableTypedArray(v) is true then wasm.typedArrayToString(v) is + returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is + returned. Else v is returned as-is. + + Reminder to self: the "return as-is" instead of returning ''+v is + arguably a design mistake but changing it is risky at this point. + */ + const flexibleString = function(v) { + if (isSQLableTypedArray(v)) return wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v, 0, v.length); + else if (Array.isArray(v)) return v.join(""); + else if (wasm.isPtr(v)) v = wasm.cstrToJs(v); + return v; + }; + /** + An Error subclass specifically for reporting Wasm-level malloc() + failure and enabling clients to unambiguously identify such + exceptions. + */ + class WasmAllocError extends Error { + /** + If called with 2 arguments and the 2nd one is an object, it + behaves like the Error constructor, else it concatenates all + arguments together with a single space between each to + construct an error message string. As a special case, if + called with no arguments then it uses a default error + message. + */ + constructor(...args) { + if (2 === args.length && "object" === typeof args[1]) super(...args); + else if (args.length) super(args.join(" ")); + else super("Allocation failed."); + this.resultCode = capi.SQLITE_NOMEM; + this.name = "WasmAllocError"; + } + } + /** + Functionally equivalent to the WasmAllocError constructor but may + be used as part of an expression, e.g.: + + ``` + return someAllocatingFunction(x) || WasmAllocError.toss(...); + ``` + */ + WasmAllocError.toss = (...args) => { + throw new WasmAllocError(...args); + }; + Object.assign(capi, { + sqlite3_bind_blob: void 0, + sqlite3_bind_text: void 0, + sqlite3_create_function_v2: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) => {}, + sqlite3_create_function: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) => {}, + sqlite3_create_window_function: (pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) => {}, + sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags, stmtPtrPtr, strPtrPtr) => {}, + sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen, stmtPtrPtr, strPtrPtr) => {}, + sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg) => {}, + sqlite3_randomness: (n, outPtr) => {} + }); + /** + Various internal-use utilities are added here as needed. They + are bound to an object only so that we have access to them in + the differently-scoped steps of the API bootstrapping + process. At the end of the API setup process, this object gets + removed. These are NOT part of the public API. + */ + const util = { + affirmBindableTypedArray, + flexibleString, + bigIntFits32, + bigIntFits64, + bigIntFitsDouble, + isBindableTypedArray, + isInt32, + isSQLableTypedArray, + isTypedArray, + isUIThread: () => globalThis.window === globalThis && !!globalThis.document, + toss: function(...args) { + throw new Error(args.join(" ")); + }, + toss3, + typedArrayPart: wasm.typedArrayPart, + assert: function(arg, msg) { + if (!arg) util.toss("Assertion failed:", msg); + }, + affirmDbHeader: function(bytes) { + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + const header = "SQLite format 3"; + if (15 > bytes.byteLength) toss3("Input does not contain an SQLite3 database header."); + for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss3("Input does not contain an SQLite3 database header."); + }, + affirmIsDb: function(bytes) { + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + const n = bytes.byteLength; + if (n < 512 || n % 512 !== 0) toss3("Byte array size", n, "is invalid for an SQLite3 db."); + util.affirmDbHeader(bytes); + } + }; + /** + wasm.X properties which are used for configuring the wasm + environment via whwashutil.js. This object gets fleshed out with + a number of WASM-specific utilities, in sqlite3-api-glue.c-pp.js. + */ + Object.assign(wasm, { + exports: config.exports || toss3("Missing API config.exports (WASM module exports)."), + memory: config.memory || config.exports["memory"] || toss3("API config object requires a WebAssembly.Memory object", "in either config.exports.memory (exported)", "or config.memory (imported)."), + pointerSize: "number" === typeof config.exports.sqlite3_libversion() ? 4 : 8, + bigIntEnabled: !!config.bigIntEnabled, + functionTable: config.functionTable, + alloc: void 0, + realloc: void 0, + dealloc: void 0 + }); + /** + wasm.alloc()'s srcTypedArray.byteLength bytes, + populates them with the values from the source + TypedArray, and returns the pointer to that memory. The + returned pointer must eventually be passed to + wasm.dealloc() to clean it up. + + The argument may be a Uint8Array, Int8Array, or ArrayBuffer, + and it throws if passed any other type. + + As a special case, to avoid further special cases where + this is used, if srcTypedArray.byteLength is 0, it + allocates a single byte and sets it to the value + 0. Even in such cases, calls must behave as if the + allocated memory has exactly srcTypedArray.byteLength + bytes. + */ + wasm.allocFromTypedArray = function(srcTypedArray) { + if (srcTypedArray instanceof ArrayBuffer) srcTypedArray = new Uint8Array(srcTypedArray); + affirmBindableTypedArray(srcTypedArray); + const pRet = wasm.alloc(srcTypedArray.byteLength || 1); + wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], Number(pRet)); + return pRet; + }; + { + const keyAlloc = config.allocExportName, keyDealloc = config.deallocExportName, keyRealloc = config.reallocExportName; + for (const key of [ + keyAlloc, + keyDealloc, + keyRealloc + ]) if (!(wasm.exports[key] instanceof Function)) toss3("Missing required exports[", key, "] function."); + wasm.alloc = function f(n) { + return f.impl(n) || WasmAllocError.toss("Failed to allocate", n, " bytes."); + }; + wasm.alloc.impl = wasm.exports[keyAlloc]; + wasm.realloc = function f(m, n) { + const m2 = f.impl(wasm.ptr.coerce(m), n); + return n ? m2 || WasmAllocError.toss("Failed to reallocate", n, " bytes.") : wasm.ptr.null; + }; + wasm.realloc.impl = wasm.exports[keyRealloc]; + wasm.dealloc = function f(m) { + f.impl(wasm.ptr.coerce(m)); + }; + wasm.dealloc.impl = wasm.exports[keyDealloc]; + } + /** + Reports info about compile-time options using + sqlite3_compileoption_get() and sqlite3_compileoption_used(). It + has several distinct uses: + + If optName is an array then it is expected to be a list of + compilation options and this function returns an object + which maps each such option to true or false, indicating + whether or not the given option was included in this + build. That object is returned. + + If optName is an object, its keys are expected to be compilation + options and this function sets each entry to true or false, + indicating whether the compilation option was used or not. That + object is returned. + + If passed no arguments then it returns an object mapping + all known compilation options to their compile-time values, + or boolean true if they are defined with no value. This + result, which is relatively expensive to compute, is cached + and returned for future no-argument calls. + + In all other cases it returns true if the given option was + active when when compiling the sqlite3 module, else false. + + Compile-time option names may optionally include their + "SQLITE_" prefix. When it returns an object of all options, + the prefix is elided. + */ + wasm.compileOptionUsed = function f(optName) { + if (!arguments.length) { + if (f._result) return f._result; + else if (!f._opt) { + f._rx = /^([^=]+)=(.+)/; + f._rxInt = /^-?\d+$/; + f._opt = function(opt, rv) { + const m = f._rx.exec(opt); + rv[0] = m ? m[1] : opt; + rv[1] = m ? f._rxInt.test(m[2]) ? +m[2] : m[2] : true; + }; + } + const rc = Object.create(null), ov = [0, 0]; + let i = 0, k; + while (k = capi.sqlite3_compileoption_get(i++)) { + f._opt(k, ov); + rc[ov[0]] = ov[1]; + } + return f._result = rc; + } else if (Array.isArray(optName)) { + const rc = Object.create(null); + optName.forEach((v) => { + rc[v] = capi.sqlite3_compileoption_used(v); + }); + return rc; + } else if ("object" === typeof optName) { + Object.keys(optName).forEach((k) => { + optName[k] = capi.sqlite3_compileoption_used(k); + }); + return optName; + } + return "string" === typeof optName ? !!capi.sqlite3_compileoption_used(optName) : false; + }; + /** + sqlite3.wasm.pstack (pseudo-stack) holds a special-case allocator + intended solely for short-lived, small data. In practice, it's + primarily used to allocate output pointers. It must not be used + for any memory which needs to outlive the scope in which it's + obtained from pstack. + + The library guarantees only that a minimum of 2kb are available + in this allocator, and it may provide more (it's a build-time + value). pstack.quota and pstack.remaining can be used to get the + total resp. remaining amount of memory. + + It has only a single intended usage pattern: + + ``` + const stackPos = pstack.pointer; + try{ + const ptr = pstack.alloc(8); + // ==> pstack.pointer === ptr + const otherPtr = pstack.alloc(8); + // ==> pstack.pointer === otherPtr + ... + }finally{ + pstack.restore(stackPos); + // ==> pstack.pointer === stackPos + } + ``` + + This allocator is much faster than a general-purpose one but is + limited to usage patterns like the one shown above (which are + pretty common when using sqlite3.capi). + + The memory lives in the WASM heap and can be used with routines + such as wasm.poke() and wasm.heap8u().slice(). + */ + wasm.pstack = Object.assign(Object.create(null), { + restore: wasm.exports.sqlite3__wasm_pstack_restore, + alloc: function(n) { + if ("string" === typeof n && !(n = wasm.sizeofIR(n))) WasmAllocError.toss("Invalid value for pstack.alloc(", arguments[0], ")"); + return wasm.exports.sqlite3__wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate", n, "bytes from the pstack."); + }, + allocChunks: function(n, sz) { + if ("string" === typeof sz && !(sz = wasm.sizeofIR(sz))) WasmAllocError.toss("Invalid size value for allocChunks(", arguments[1], ")"); + const mem = wasm.pstack.alloc(n * sz); + const rc = [mem]; + let i = 1, offset = sz; + for (; i < n; ++i, offset += sz) rc.push(wasm.ptr.add(mem, offset)); + return rc; + }, + allocPtr: (n = 1, safePtrSize = true) => { + return 1 === n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptr.size) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptr.size); + }, + call: function(f) { + const stackPos = wasm.pstack.pointer; + try { + return f(sqlite3); + } finally { + wasm.pstack.restore(stackPos); + } + } + }); + Object.defineProperties(wasm.pstack, { + pointer: { + configurable: false, + iterable: true, + writeable: false, + get: wasm.exports.sqlite3__wasm_pstack_ptr + }, + quota: { + configurable: false, + iterable: true, + writeable: false, + get: wasm.exports.sqlite3__wasm_pstack_quota + }, + remaining: { + configurable: false, + iterable: true, + writeable: false, + get: wasm.exports.sqlite3__wasm_pstack_remaining + } + }); + /** + Docs: https://sqlite.org/wasm/doc/trunk/api-c-style.md#sqlite3_randomness + */ + capi.sqlite3_randomness = (...args) => { + if (1 === args.length && util.isTypedArray(args[0]) && 1 === args[0].BYTES_PER_ELEMENT) { + const ta = args[0]; + if (0 === ta.byteLength) { + wasm.exports.sqlite3_randomness(0, wasm.ptr.null); + return ta; + } + const stack = wasm.pstack.pointer; + try { + let n = ta.byteLength, offset = 0; + const r = wasm.exports.sqlite3_randomness; + const heap = wasm.heap8u(); + const nAlloc = n < 512 ? n : 512; + const ptr = wasm.pstack.alloc(nAlloc); + do { + const j = n > nAlloc ? nAlloc : n; + r(j, ptr); + ta.set(wasm.typedArrayPart(heap, ptr, wasm.ptr.add(ptr, j)), offset); + n -= j; + offset += j; + } while (n > 0); + } catch (e) { + config.error("Highly unexpected (and ignored!) exception in sqlite3_randomness():", e); + } finally { + wasm.pstack.restore(stack); + } + return ta; + } + wasm.exports.sqlite3_randomness(...args); + }; + /** + If the wasm environment has a WASMFS/OPFS-backed persistent + storage directory, its path is returned by this function. If it + does not then it returns "" (noting that "" is a falsy value). + + The first time this is called, this function inspects the current + environment to determine whether WASMFS persistence support is + available and, if it is, enables it (if needed). After the first + call it always returns the cached result. + + If the returned string is not empty, any files stored under the + returned path (recursively) are housed in OPFS storage. If the + returned string is empty, this particular persistent storage + option is not available on the client. + + Though the mount point name returned by this function is intended + to remain stable, clients should not hard-coded it anywhere. + Always call this function to get the path. + + This function is a no-op in most builds of this library, as the + WASMFS capability requires a custom build. + */ + capi.sqlite3_wasmfs_opfs_dir = function() { + if (void 0 !== this.dir) return this.dir; + const pdir = config.wasmfsOpfsDir; + if (!pdir || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !wasm.exports.sqlite3__wasm_init_wasmfs) return this.dir = ""; + try { + if (pdir && 0 === wasm.xCallWrapped("sqlite3__wasm_init_wasmfs", "i32", ["string"], pdir)) return this.dir = pdir; + else return this.dir = ""; + } catch (e) { + return this.dir = ""; + } + }.bind(Object.create(null)); + /** + Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a + non-empty string and the given name starts with (that string + + '/'), else returns false. + */ + capi.sqlite3_wasmfs_filename_is_persistent = function(name) { + const p = capi.sqlite3_wasmfs_opfs_dir(); + return p && name ? name.startsWith(p + "/") : false; + }; + /** + Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name + (defaulting to "main"), returns a truthy value (see below) if + that db uses that VFS, else returns false. If pDb is falsy then + the 3rd argument is ignored and this function returns a truthy + value if the default VFS name matches that of the 2nd argument. + Results are undefined if pDb is truthy but refers to an invalid + pointer. The 3rd argument specifies the database name of the + given database connection to check, defaulting to the main db. + + The 2nd and 3rd arguments may either be a JS string or a WASM + C-string. If the 2nd argument is a NULL WASM pointer, the default + VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is + assumed. + + The truthy value it returns is a pointer to the `sqlite3_vfs` + object. + + To permit safe use of this function from APIs which may be called + via C (like SQL UDFs), this function does not throw: if bad + arguments cause a conversion error when passing into wasm-space, + false is returned. + */ + capi.sqlite3_js_db_uses_vfs = function(pDb, vfsName, dbName = 0) { + try { + const pK = capi.sqlite3_vfs_find(vfsName); + if (!pK) return false; + else if (!pDb) return pK === capi.sqlite3_vfs_find(0) ? pK : false; + else return pK === capi.sqlite3_js_db_vfs(pDb, dbName) ? pK : false; + } catch (e) { + return false; + } + }; + /** + Returns an array of the names of all currently-registered sqlite3 + VFSes. + */ + capi.sqlite3_js_vfs_list = function() { + const rc = []; + let pVfs = capi.sqlite3_vfs_find(wasm.ptr.null); + while (pVfs) { + const oVfs = new capi.sqlite3_vfs(pVfs); + rc.push(wasm.cstrToJs(oVfs.$zName)); + pVfs = oVfs.$pNext; + oVfs.dispose(); + } + return rc; + }; + /** + A convenience wrapper around sqlite3_serialize() which serializes + the given `sqlite3*` pointer to a Uint8Array. The first argument + may be either an `sqlite3*` or an sqlite3.oo1.DB instance. + + On success it returns a Uint8Array. If the schema is empty, an + empty array is returned. + + `schema` is the schema to serialize. It may be a WASM C-string + pointer or a JS string. If it is falsy, it defaults to `"main"`. + + On error it throws with a description of the problem. + */ + capi.sqlite3_js_db_export = function(pDb, schema = 0) { + pDb = wasm.xWrap.testConvertArg("sqlite3*", pDb); + if (!pDb) toss3("Invalid sqlite3* argument."); + if (!wasm.bigIntEnabled) toss3("BigInt support is not enabled."); + const scope = wasm.scopedAllocPush(); + let pOut; + try { + const pSize = wasm.scopedAlloc(8 + wasm.ptr.size); + const ppOut = wasm.ptr.add(pSize, 8); + /** + Maintenance reminder, since this cost a full hour of grief + and confusion: if the order of pSize/ppOut are reversed in + that memory block, fetching the value of pSize after the + export reads a garbage size because it's not on an 8-byte + memory boundary! + */ + const zSchema = schema ? wasm.isPtr(schema) ? schema : wasm.scopedAllocCString("" + schema) : wasm.ptr.null; + let rc = wasm.exports.sqlite3__wasm_db_serialize(pDb, zSchema, ppOut, pSize, 0); + if (rc) toss3("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc)); + pOut = wasm.peekPtr(ppOut); + const nOut = wasm.peek(pSize, "i64"); + rc = nOut ? wasm.heap8u().slice(Number(pOut), Number(pOut) + Number(nOut)) : new Uint8Array(); + return rc; + } finally { + if (pOut) wasm.exports.sqlite3_free(pOut); + wasm.scopedAllocPop(scope); + } + }; + /** + Given a `sqlite3*` and a database name (JS string or WASM + C-string pointer, which may be 0), returns a pointer to the + sqlite3_vfs responsible for it. If the given db name is null/0, + or not provided, then "main" is assumed. + */ + capi.sqlite3_js_db_vfs = (dbPointer, dbName = wasm.ptr.null) => util.sqlite3__wasm_db_vfs(dbPointer, dbName); + /** + A thin wrapper around capi.sqlite3_aggregate_context() which + behaves the same except that it throws a WasmAllocError if that + function returns 0. As a special case, if n is falsy it does + _not_ throw if that function returns 0. That special case is + intended for use with xFinal() implementations. + */ + capi.sqlite3_js_aggregate_context = (pCtx, n) => { + return capi.sqlite3_aggregate_context(pCtx, n) || (n ? WasmAllocError.toss("Cannot allocate", n, "bytes for sqlite3_aggregate_context()") : 0); + }; + /** + If the current environment supports the POSIX file APIs, this routine + creates (or overwrites) the given file using those APIs. This is + primarily intended for use in Emscripten-based builds where the POSIX + APIs are transparently proxied by an in-memory virtual filesystem. + It may behave differently in other environments. + + The first argument must be either a JS string or WASM C-string + holding the filename. This routine does _not_ create intermediary + directories if the filename has a directory part. + + The 2nd argument may either a valid WASM memory pointer, an + ArrayBuffer, or a Uint8Array. The 3rd must be the length, in + bytes, of the data array to copy. If the 2nd argument is an + ArrayBuffer or Uint8Array and the 3rd is not a positive integer + then the 3rd defaults to the array's byteLength value. + + Results are undefined if data is a WASM pointer and dataLen is + exceeds data's bounds. + + Throws if any arguments are invalid or if creating or writing to + the file fails. + + Added in 3.43 as an alternative for the deprecated + sqlite3_js_vfs_create_file(). + */ + capi.sqlite3_js_posix_create_file = function(filename, data, dataLen) { + let pData; + if (data && wasm.isPtr(data)) pData = data; + else if (data instanceof ArrayBuffer || data instanceof Uint8Array) { + pData = wasm.allocFromTypedArray(data); + if (arguments.length < 3 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; + } else SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file()."); + try { + if (!util.isInt32(dataLen) || dataLen < 0) SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file()."); + const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen); + if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); + } finally { + if (pData && pData !== data) wasm.dealloc(pData); + } + }; + /** + Deprecation warning: this function does not work properly in + debug builds of sqlite3 because its out-of-scope use of the + sqlite3_vfs API triggers assertions in the core library. That + was unfortunately not discovered until 2023-08-11. This function + is now deprecated. It should not be used in new code and should + be removed from existing code. + + Alternative options: + + - The "unix" VFS and its variants can get equivalent + functionality with sqlite3_js_posix_create_file(). + + - OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs" + VFS, or the importDb() method of the PoolUtil object provided + by the "opfs-sahpool" OPFS (noting that its VFS name may differ + depending on client-side configuration). We cannot proxy those + from here because the former is necessarily asynchronous and + the latter requires information not available to this function. + + Historical (deprecated) behaviour: + + Creates a file using the storage appropriate for the given + sqlite3_vfs. The first argument may be a VFS name (JS string + only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or + a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the + default VFS. If passed a string which does not resolve using + sqlite3_vfs_find(), an exception is thrown. (Note that a WASM + C-string is not accepted because it is impossible to + distinguish from a C-level `sqlite3_vfs*`.) + + The second argument, the filename, must be a JS or WASM C-string. + + The 3rd may either be falsy, a valid WASM memory pointer, an + ArrayBuffer, or a Uint8Array. The 4th must be the length, in + bytes, of the data array to copy. If the 3rd argument is an + ArrayBuffer or Uint8Array and the 4th is not a positive integer + then the 4th defaults to the array's byteLength value. + + If data is falsy then a file is created with dataLen bytes filled + with uninitialized data (whatever truncate() leaves there). If + data is not falsy then a file is created or truncated and it is + filled with the first dataLen bytes of the data source. + + Throws if any arguments are invalid or if creating or writing to + the file fails. + + Note that most VFSes do _not_ automatically create directory + parts of filenames, nor do all VFSes have a concept of + directories. If the given filename is not valid for the given + VFS, an exception will be thrown. This function exists primarily + to assist in implementing file-upload capability, with the caveat + that clients must have some idea of the VFS into which they want + to upload and that VFS must support the operation. + + VFS-specific notes: + + - "memdb": results are undefined. + + - "kvvfs": will fail with an I/O error due to strict internal + requirements of that VFS's xTruncate(). + + - "unix" and related: will use the WASM build's equivalent of the + POSIX I/O APIs. This will work so long as neither a specific + VFS nor the WASM environment imposes requirements which break + it. (Much later: it turns out that debug builds of the library + impose such requirements, in that they assert() that dataLen is + an even multiple of a valid db page size.) + + - "opfs": uses OPFS storage and creates directory parts of the + filename. It can only be used to import an SQLite3 database + file and will fail if given anything else. + */ + capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen) { + config.warn("sqlite3_js_vfs_create_file() is deprecated and", "should be avoided because it can lead to C-level crashes.", "See its documentation for alternatives."); + let pData; + if (data) if (wasm.isPtr(data)) pData = data; + else { + if (data instanceof ArrayBuffer) data = new Uint8Array(data); + if (data instanceof Uint8Array) { + pData = wasm.allocFromTypedArray(data); + if (arguments.length < 4 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; + } else SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file()."); + } + else pData = 0; + if (!util.isInt32(dataLen) || dataLen < 0) { + if (pData && pData !== data) wasm.dealloc(pData); + SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file()."); + } + try { + const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen); + if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); + } finally { + if (pData && pData !== data) wasm.dealloc(pData); + } + }; + /** + Converts SQL input from a variety of convenient formats + to plain strings. + + If v is a string, it is returned as-is. If it is-a Array, its + join("") result is returned. If is is a Uint8Array, Int8Array, + or ArrayBuffer, it is assumed to hold UTF-8-encoded text and is + decoded to a string. If it looks like a WASM pointer, + wasm.cstrToJs(sql) is returned. Else undefined is returned. + + Added in 3.44 + */ + capi.sqlite3_js_sql_to_string = (sql) => { + if ("string" === typeof sql) return sql; + const x = flexibleString(v); + return x === v ? void 0 : x; + }; + /** + Wraps all known variants of the C-side variadic + sqlite3_db_config(). + + Full docs: https://sqlite.org/c3ref/db_config.html + + Returns capi.SQLITE_MISUSE if op is not a valid operation ID. + + The variants which take `(int, int*)` arguments treat a + missing or falsy pointer argument as 0. + */ + capi.sqlite3_db_config = function(pDb, op, ...args) { + switch (op) { + case capi.SQLITE_DBCONFIG_ENABLE_FKEY: + case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: + case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: + case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: + case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: + case capi.SQLITE_DBCONFIG_ENABLE_QPSG: + case capi.SQLITE_DBCONFIG_TRIGGER_EQP: + case capi.SQLITE_DBCONFIG_RESET_DATABASE: + case capi.SQLITE_DBCONFIG_DEFENSIVE: + case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA: + case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: + case capi.SQLITE_DBCONFIG_DQS_DML: + case capi.SQLITE_DBCONFIG_DQS_DDL: + case capi.SQLITE_DBCONFIG_ENABLE_VIEW: + case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: + case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA: + case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS: + case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER: + case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: + case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: + case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: + if (!this.ip) this.ip = wasm.xWrap("sqlite3__wasm_db_config_ip", "int", [ + "sqlite3*", + "int", + "int", + "*" + ]); + return this.ip(pDb, op, args[0], args[1] || 0); + case capi.SQLITE_DBCONFIG_LOOKASIDE: + if (!this.pii) this.pii = wasm.xWrap("sqlite3__wasm_db_config_pii", "int", [ + "sqlite3*", + "int", + "*", + "int", + "int" + ]); + return this.pii(pDb, op, args[0], args[1], args[2]); + case capi.SQLITE_DBCONFIG_MAINDBNAME: + if (!this.s) this.s = wasm.xWrap("sqlite3__wasm_db_config_s", "int", [ + "sqlite3*", + "int", + "string:static" + ]); + return this.s(pDb, op, args[0]); + default: return capi.SQLITE_MISUSE; + } + }.bind(Object.create(null)); + /** + Given a (sqlite3_value*), this function attempts to convert it + to an equivalent JS value with as much fidelity as feasible and + return it. + + By default it throws if it cannot determine any sensible + conversion. If passed a falsy second argument, it instead returns + `undefined` if no suitable conversion is found. Note that there + is no conversion from SQL to JS which results in the `undefined` + value, so `undefined` has an unambiguous meaning here. It will + always throw a WasmAllocError if allocating memory for a + conversion fails. + + Caveats: + + - It does not support sqlite3_value_to_pointer() conversions + because those require a type name string which this function + does not have and cannot sensibly be given at the level of the + API where this is used (e.g. automatically converting UDF + arguments). Clients using sqlite3_value_to_pointer(), and its + related APIs, will need to manage those themselves. + */ + capi.sqlite3_value_to_js = function(pVal, throwIfCannotConvert = true) { + let arg; + const valType = capi.sqlite3_value_type(pVal); + switch (valType) { + case capi.SQLITE_INTEGER: + if (wasm.bigIntEnabled) { + arg = capi.sqlite3_value_int64(pVal); + if (util.bigIntFitsDouble(arg)) arg = Number(arg); + } else arg = capi.sqlite3_value_double(pVal); + break; + case capi.SQLITE_FLOAT: + arg = capi.sqlite3_value_double(pVal); + break; + case capi.SQLITE_TEXT: + arg = capi.sqlite3_value_text(pVal); + break; + case capi.SQLITE_BLOB: { + const n = capi.sqlite3_value_bytes(pVal); + const pBlob = capi.sqlite3_value_blob(pVal); + if (n && !pBlob) sqlite3.WasmAllocError.toss("Cannot allocate memory for blob argument of", n, "byte(s)"); + arg = n ? wasm.heap8u().slice(Number(pBlob), Number(pBlob) + Number(n)) : null; + break; + } + case capi.SQLITE_NULL: + arg = null; + break; + default: + if (throwIfCannotConvert) toss3(capi.SQLITE_MISMATCH, "Unhandled sqlite3_value_type():", valType); + arg = void 0; + } + return arg; + }; + /** + Requires a C-style array of `sqlite3_value*` objects and the + number of entries in that array. Returns a JS array containing + the results of passing each C array entry to + sqlite3_value_to_js(). The 3rd argument to this function is + passed on as the 2nd argument to that one. + */ + capi.sqlite3_values_to_js = function(argc, pArgv, throwIfCannotConvert = true) { + let i; + const tgt = []; + for (i = 0; i < argc; ++i) + /** + Curiously: despite ostensibly requiring 8-byte + alignment, the pArgv array is parcelled into chunks of + 4 bytes (1 pointer each). The values those point to + have 8-byte alignment but the individual argv entries + do not. + */ + tgt.push(capi.sqlite3_value_to_js(wasm.peekPtr(wasm.ptr.add(pArgv, wasm.ptr.size * i)), throwIfCannotConvert)); + return tgt; + }; + /** + Calls either sqlite3_result_error_nomem(), if e is-a + WasmAllocError, or sqlite3_result_error(). In the latter case, + the second argument is coerced to a string to create the error + message. + + The first argument is a (sqlite3_context*). Returns void. + Does not throw. + */ + capi.sqlite3_result_error_js = function(pCtx, e) { + if (e instanceof WasmAllocError) capi.sqlite3_result_error_nomem(pCtx); + else capi.sqlite3_result_error(pCtx, "" + e, -1); + }; + /** + This function passes its 2nd argument to one of the + sqlite3_result_xyz() routines, depending on the type of that + argument: + + - If (val instanceof Error), this function passes it to + sqlite3_result_error_js(). + - `null`: `sqlite3_result_null()` + - `boolean`: `sqlite3_result_int()` with a value of 0 or 1. + - `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or + `sqlite3_result_double()`, depending on the range of the number + and whether or not int64 support is enabled. + - `bigint`: similar to `number` but will trigger an error if the + value is too big to store in an int64. + - `string`: `sqlite3_result_text()` + - Uint8Array or Int8Array or ArrayBuffer: `sqlite3_result_blob()` + - `undefined`: is a no-op provided to simplify certain use cases. + + Anything else triggers `sqlite3_result_error()` with a + description of the problem. + + The first argument to this function is a `(sqlite3_context*)`. + Returns void. Does not throw. + */ + capi.sqlite3_result_js = function(pCtx, val) { + if (val instanceof Error) { + capi.sqlite3_result_error_js(pCtx, val); + return; + } + try { + switch (typeof val) { + case "undefined": break; + case "boolean": + capi.sqlite3_result_int(pCtx, val ? 1 : 0); + break; + case "bigint": + if (util.bigIntFits32(val)) capi.sqlite3_result_int(pCtx, Number(val)); + else if (util.bigIntFitsDouble(val)) capi.sqlite3_result_double(pCtx, Number(val)); + else if (wasm.bigIntEnabled) if (util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); + else toss3("BigInt value", val.toString(), "is too BigInt for int64."); + else toss3("BigInt value", val.toString(), "is too BigInt."); + break; + case "number": { + let f; + if (util.isInt32(val)) f = capi.sqlite3_result_int; + else if (wasm.bigIntEnabled && Number.isInteger(val) && util.bigIntFits64(BigInt(val))) f = capi.sqlite3_result_int64; + else f = capi.sqlite3_result_double; + f(pCtx, val); + break; + } + case "string": { + const [p, n] = wasm.allocCString(val, true); + capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC); + break; + } + case "object": if (null === val) { + capi.sqlite3_result_null(pCtx); + break; + } else if (util.isBindableTypedArray(val)) { + const pBlob = wasm.allocFromTypedArray(val); + capi.sqlite3_result_blob(pCtx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); + break; + } + default: toss3("Don't not how to handle this UDF result value:", typeof val, val); + } + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + /** + Returns the result sqlite3_column_value(pStmt,iCol) passed to + sqlite3_value_to_js(). The 3rd argument of this function is + ignored by this function except to pass it on as the second + argument of sqlite3_value_to_js(). If the sqlite3_column_value() + returns NULL (e.g. because the column index is out of range), + this function returns `undefined`, regardless of the 3rd + argument. If the 3rd argument is falsy and conversion fails, + `undefined` will be returned. + + Note that sqlite3_column_value() returns an "unprotected" value + object, but in a single-threaded environment (like this one) + there is no distinction between protected and unprotected values. + */ + capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert = true) { + const v = capi.sqlite3_column_value(pStmt, iCol); + return 0 === v ? void 0 : capi.sqlite3_value_to_js(v, throwIfCannotConvert); + }; + { + /** + Internal impl of sqlite3_preupdate_new/old_js() and + sqlite3changeset_new/old_js(). + */ + const __newOldValue = function(pObj, iCol, impl) { + impl = capi[impl]; + if (!this.ptr) this.ptr = wasm.allocPtr(); + else wasm.pokePtr(this.ptr, 0); + const rc = impl(pObj, iCol, this.ptr); + if (rc) return SQLite3Error.toss(rc, arguments[2] + "() failed with code " + rc); + const pv = wasm.peekPtr(this.ptr); + return pv ? capi.sqlite3_value_to_js(pv, true) : void 0; + }.bind(Object.create(null)); + /** + A wrapper around sqlite3_preupdate_new() which fetches the + sqlite3_value at the given index and returns the result of + passing it to sqlite3_value_to_js(). Throws on error. + */ + capi.sqlite3_preupdate_new_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_new"); + /** + The sqlite3_preupdate_old() counterpart of + sqlite3_preupdate_new_js(), with an identical interface. + */ + capi.sqlite3_preupdate_old_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_old"); + /** + A wrapper around sqlite3changeset_new() which fetches the + sqlite3_value at the given index and returns the result of + passing it to sqlite3_value_to_js(). Throws on error. + + If sqlite3changeset_new() succeeds but has no value to report, + this function returns the undefined value, noting that + undefined is not a valid conversion from an `sqlite3_value`, so + is unambiguous. + */ + capi.sqlite3changeset_new_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_new"); + /** + The sqlite3changeset_old() counterpart of + sqlite3changeset_new_js(), with an identical interface. + */ + capi.sqlite3changeset_old_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_old"); + } + const sqlite3 = { + WasmAllocError, + SQLite3Error, + capi, + util, + wasm, + config, + version: Object.create(null), + client: void 0, + asyncPostInit: async function ff() { + if (ff.isReady instanceof Promise) return ff.isReady; + let lia = this.initializersAsync; + delete this.initializersAsync; + const postInit = async () => { + if (!sqlite3.__isUnderTest) { + delete sqlite3.util; + delete sqlite3.StructBinder; + } + return sqlite3; + }; + const catcher = (e) => { + config.error("an async sqlite3 initializer failed:", e); + throw e; + }; + if (!lia || !lia.length) return ff.isReady = postInit().catch(catcher); + lia = lia.map((f) => { + return f instanceof Function ? async (x) => f(sqlite3) : f; + }); + lia.push(postInit); + let p = Promise.resolve(sqlite3); + while (lia.length) p = p.then(lia.shift()); + return ff.isReady = p.catch(catcher); + }.bind(sqlite3ApiBootstrap), + scriptInfo: void 0 + }; + if ("undefined" !== typeof sqlite3IsUnderTest) sqlite3.__isUnderTest = !!sqlite3IsUnderTest; + try { + sqlite3ApiBootstrap.initializers.forEach((f) => { + f(sqlite3); + }); + } catch (e) { + console.error("sqlite3 bootstrap initializer threw:", e); + throw e; + } + delete sqlite3ApiBootstrap.initializers; + sqlite3ApiBootstrap.sqlite3 = sqlite3; + if ("undefined" !== typeof sqlite3InitScriptInfo) { + sqlite3InitScriptInfo.debugModule("sqlite3ApiBootstrap() complete", sqlite3); + sqlite3.scriptInfo = sqlite3InitScriptInfo; + } + if (sqlite3.__isUnderTest) { + if ("undefined" !== typeof EmscriptenModule) sqlite3.config.emscripten = EmscriptenModule; + const iw = sqlite3.scriptInfo?.instantiateWasm; + if (iw) { + sqlite3.wasm.module = iw.module; + sqlite3.wasm.instance = iw.instance; + sqlite3.wasm.imports = iw.imports; + } + } + /** + Eliminate any confusion about whether these config objects may + be used after library initialization by eliminating the outward-facing + objects... + */ + delete globalThis.sqlite3ApiConfig; + delete globalThis.sqlite3ApiBootstrap; + delete sqlite3ApiBootstrap.defaultConfig; + return sqlite3.asyncPostInit().then((s) => { + if ("undefined" !== typeof sqlite3InitScriptInfo) sqlite3InitScriptInfo.debugModule("sqlite3.asyncPostInit() complete", s); + delete s.asyncPostInit; + delete s.scriptInfo; + delete s.emscripten; + return s; + }); + }; + /** + globalThis.sqlite3ApiBootstrap.initializers is an internal detail + used by the various pieces of the sqlite3 API's amalgamation + process. It must not be modified by client code except when plugging + such code into the amalgamation process. + + Each component of the amalgamation is expected to append a function + to this array. When sqlite3ApiBootstrap() is called for the first + time, each such function will be called (in their appended order) + and passed the sqlite3 namespace object, into which they can install + their features. At the end of that process, this array is deleted. + + The order of insertion into this array is significant for + some pieces. e.g. sqlite3.capi and sqlite3.wasm cannot be fully + utilized until the whwasmutil.js part is plugged in via + sqlite3-api-glue.js. + */ + globalThis.sqlite3ApiBootstrap.initializers = []; + /** + globalThis.sqlite3ApiBootstrap.initializersAsync is an internal detail + used by the sqlite3 API's amalgamation process. It must not be + modified by client code except when plugging such code into the + amalgamation process. + + The counterpart of globalThis.sqlite3ApiBootstrap.initializers, + specifically for initializers which are asynchronous. All entries in + this list must be either async functions, non-async functions which + return a Promise, or a Promise. Each function in the list is called + with the sqlite3 object as its only argument. + + The resolved value of any Promise is ignored and rejection will kill + the asyncPostInit() process (at an indeterminate point because all + of them are run asynchronously in parallel). + + This list is not processed until the client calls + sqlite3.asyncPostInit(). This means, for example, that intializers + added to globalThis.sqlite3ApiBootstrap.initializers may push entries to + this list. + */ + globalThis.sqlite3ApiBootstrap.initializersAsync = []; + /** + Client code may assign sqlite3ApiBootstrap.defaultConfig an + object-type value before calling sqlite3ApiBootstrap() (without + arguments) in order to tell that call to use this object as its + default config value. The intention of this is to provide + downstream clients with a reasonably flexible approach for plugging in + an environment-suitable configuration without having to define a new + global-scope symbol. + */ + globalThis.sqlite3ApiBootstrap.defaultConfig = Object.create(null); + /** + Placeholder: gets installed by the first call to + globalThis.sqlite3ApiBootstrap(). However, it is recommended that the + caller of sqlite3ApiBootstrap() capture its return value and delete + globalThis.sqlite3ApiBootstrap after calling it. It returns the same + value which will be stored here. + */ + globalThis.sqlite3ApiBootstrap.sqlite3 = void 0; + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + sqlite3.version = { + "libVersion": "3.52.0", + "libVersionNumber": 3052e3, + "sourceId": "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", + "downloadVersion": 352e4, + "scm": { + "sha3-256": "407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", + "branch": "trunk", + "tags": "", + "datetime": "2026-01-30T06:37:34.096Z" + } + }; + }); + globalThis.WhWasmUtilInstaller = function WhWasmUtilInstaller(target) { + "use strict"; + if (void 0 === target.bigIntEnabled) target.bigIntEnabled = !!globalThis["BigInt64Array"]; + /** Throws a new Error, the message of which is the concatenation of + all args with a space between each. */ + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + if (!target.pointerSize && !target.pointerIR && target.alloc && target.dealloc) { + const ptr = target.alloc(1); + target.pointerSize = "bigint" === typeof ptr ? 8 : 4; + target.dealloc(ptr); + } + /** + As of 2025-09-21, this library works with 64-bit WASM modules + built with Emscripten's -sMEMORY64=1. + */ + if (target.pointerSize && !target.pointerIR) target.pointerIR = 4 === target.pointerSize ? "i32" : "i64"; + const __ptrIR = target.pointerIR ??= "i32"; + const __ptrSize = target.pointerSize ??= "i32" === __ptrIR ? 4 : "i64" === __ptrIR ? 8 : 0; + delete target.pointerSize; + delete target.pointerIR; + if ("i32" !== __ptrIR && "i64" !== __ptrIR) toss("Invalid pointerIR:", __ptrIR); + else if (8 !== __ptrSize && 4 !== __ptrSize) toss("Invalid pointerSize:", __ptrSize); + /** Either BigInt or, if !target.bigIntEnabled, a function which + throws complaining that BigInt is not enabled. */ + const __BigInt = target.bigIntEnabled ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); + const __Number = (v) => Number(v || 0); + /** + If target.ptr.ir==='i32' then this is equivalent to + Number(v||0) else it's equivalent to BigInt(v||0), throwing + if BigInt support is disabled. + + Why? Because Number(null)===0, but BigInt(null) throws. We + perform the same for Number to allow the undefined value to be + treated as a NULL WASM pointer, primarily to reduce friction in + many SQLite3 bindings which have long relied on that. + */ + const __asPtrType = 4 === __ptrSize ? __Number : __BigInt; + /** + The number 0 as either type Number or BigInt, depending on + target.ptr.ir. + */ + const __NullPtr = __asPtrType(0); + /** + Expects any number of numeric arguments, each one of either type + Number or BigInt. It sums them up (from an implicit starting + point of 0 or 0n) and returns them as a number of the same type + which target.ptr.coerce() uses. + + This is a workaround for not being able to mix Number/BigInt in + addition/subtraction expressions (which we frequently need for + calculating pointer offsets). + */ + const __ptrAdd = function(...args) { + let rc = __asPtrType(0); + for (const v of args) rc += __asPtrType(v); + return rc; + }; + /** Set up target.ptr... */ + { + const __ptr = Object.create(null); + Object.defineProperty(target, "ptr", { + enumerable: true, + get: () => __ptr, + set: () => toss("The ptr property is read-only.") + }); + (function f(name, val) { + Object.defineProperty(__ptr, name, { + enumerable: true, + get: () => val, + set: () => toss("ptr[" + name + "] is read-only.") + }); + return f; + })("null", __NullPtr)("size", __ptrSize)("ir", __ptrIR)("coerce", __asPtrType)("add", __ptrAdd)("addn", 4 === __ptrIR ? __ptrAdd : (...args) => Number(__ptrAdd(...args))); + } + if (!target.exports) Object.defineProperty(target, "exports", { + enumerable: true, + configurable: true, + get: () => target.instance?.exports + }); + /** Stores various cached state. */ + const cache = Object.create(null); + /** Previously-recorded size of cache.memory.buffer, noted so that + we can recreate the view objects if the heap grows. */ + cache.heapSize = 0; + /** WebAssembly.Memory object extracted from target.memory or + target.exports.memory the first time heapWrappers() is + called. */ + cache.memory = null; + /** uninstallFunction() puts table indexes in here for reuse and + installFunction() extracts them. */ + cache.freeFuncIndexes = []; + /** + List-of-lists used by scopedAlloc() and friends. + */ + cache.scopedAlloc = []; + /** Push the pointer ptr to the current cache.scopedAlloc list + (which must already exist) and return ptr. */ + cache.scopedAlloc.pushPtr = (ptr) => { + cache.scopedAlloc[cache.scopedAlloc.length - 1].push(ptr); + return ptr; + }; + cache.utf8Decoder = new TextDecoder(); + cache.utf8Encoder = new TextEncoder("utf-8"); + /** + For the given IR-like string in the set ('i8', 'i16', 'i32', + 'f32', 'float', 'i64', 'f64', 'double', '*'), or any string value + ending in '*', returns the sizeof for that value + (target.ptr.size in the latter case). For any other value, it + returns the undefined value. + */ + target.sizeofIR = (n) => { + switch (n) { + case "i8": return 1; + case "i16": return 2; + case "i32": + case "f32": + case "float": return 4; + case "i64": + case "f64": + case "double": return 8; + case "*": return __ptrSize; + default: return ("" + n).endsWith("*") ? __ptrSize : void 0; + } + }; + /** + If (cache.heapSize !== cache.memory.buffer.byteLength), i.e. if + the heap has grown since the last call, updates cache.HEAPxyz. + Returns the cache object. + */ + const heapWrappers = function() { + if (!cache.memory) cache.memory = target.memory instanceof WebAssembly.Memory ? target.memory : target.exports.memory; + else if (cache.heapSize === cache.memory.buffer.byteLength) return cache; + const b = cache.memory.buffer; + cache.HEAP8 = new Int8Array(b); + cache.HEAP8U = new Uint8Array(b); + cache.HEAP16 = new Int16Array(b); + cache.HEAP16U = new Uint16Array(b); + cache.HEAP32 = new Int32Array(b); + cache.HEAP32U = new Uint32Array(b); + cache.HEAP32F = new Float32Array(b); + cache.HEAP64F = new Float64Array(b); + if (target.bigIntEnabled) if ("undefined" !== typeof BigInt64Array) { + cache.HEAP64 = new BigInt64Array(b); + cache.HEAP64U = new BigUint64Array(b); + } else toss("BigInt support is enabled, but the BigInt64Array type is missing."); + cache.heapSize = b.byteLength; + return cache; + }; + /** Convenience equivalent of this.heapForSize(8,false). */ + target.heap8 = () => heapWrappers().HEAP8; + /** Convenience equivalent of this.heapForSize(8,true). */ + target.heap8u = () => heapWrappers().HEAP8U; + /** Convenience equivalent of this.heapForSize(16,false). */ + target.heap16 = () => heapWrappers().HEAP16; + /** Convenience equivalent of this.heapForSize(16,true). */ + target.heap16u = () => heapWrappers().HEAP16U; + /** Convenience equivalent of this.heapForSize(32,false). */ + target.heap32 = () => heapWrappers().HEAP32; + /** Convenience equivalent of this.heapForSize(32,true). */ + target.heap32u = () => heapWrappers().HEAP32U; + /** + Requires n to be one of: + + - integer 8, 16, or 32. + - A integer-type TypedArray constructor: Int8Array, Int16Array, + Int32Array, or their Uint counterparts. + + If this.bigIntEnabled is true, it also accepts the value 64 or a + BigInt64Array/BigUint64Array, else it throws if passed 64 or one + of those constructors. + + Returns an integer-based TypedArray view of the WASM heap memory + buffer associated with the given block size. If passed an integer + as the first argument and unsigned is truthy then the "U" + (unsigned) variant of that view is returned, else the signed + variant is returned. If passed a TypedArray value, the 2nd + argument is ignored. Float32Array and Float64Array views are not + supported by this function. + + Growth of the heap will invalidate any references to this heap, + so do not hold a reference longer than needed and do not use a + reference after any operation which may allocate. Instead, + re-fetch the reference by calling this function again. + + Throws if passed an invalid n. + */ + target.heapForSize = function(n, unsigned = true) { + const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); + switch (n) { + case Int8Array: return c.HEAP8; + case Uint8Array: return c.HEAP8U; + case Int16Array: return c.HEAP16; + case Uint16Array: return c.HEAP16U; + case Int32Array: return c.HEAP32; + case Uint32Array: return c.HEAP32U; + case 8: return unsigned ? c.HEAP8U : c.HEAP8; + case 16: return unsigned ? c.HEAP16U : c.HEAP16; + case 32: return unsigned ? c.HEAP32U : c.HEAP32; + case 64: + if (c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64; + break; + default: if (target.bigIntEnabled) { + if (n === globalThis["BigUint64Array"]) return c.HEAP64U; + else if (n === globalThis["BigInt64Array"]) return c.HEAP64; + break; + } + } + toss("Invalid heapForSize() size: expecting 8, 16, 32,", "or (if BigInt is enabled) 64."); + }; + const __funcTable = target.functionTable; + delete target.functionTable; + /** + Returns the WASM-exported "indirect function table". + */ + target.functionTable = __funcTable ? () => __funcTable : () => target.exports.__indirect_function_table; + /** + Given a function pointer, returns the WASM function table entry + if found, else returns a falsy value: undefined if fptr is out of + range or null if it's in range but the table entry is empty. + */ + target.functionEntry = function(fptr) { + const ft = target.functionTable(); + return fptr < ft.length ? ft.get(__asPtrType(fptr)) : void 0; + }; + /** + Creates a WASM function which wraps the given JS function and + returns the JS binding of that WASM function. The signature + string must be the Jaccwabyt-format or Emscripten + addFunction()-format function signature string. In short: in may + have one of the following formats: + + - Emscripten: `"x..."`, where the first x is a letter representing + the result type and subsequent letters represent the argument + types. Functions with no arguments have only a single + letter. + + - Jaccwabyt: `"x(...)"` where `x` is the letter representing the + result type and letters in the parens (if any) represent the + argument types. Functions with no arguments use `x()`. + + Supported letters: + + - `i` = int32 + - `p` = int32 or int64 ("pointer"), depending on target.ptr.size + - `j` = int64 + - `f` = float32 + - `d` = float64 + - `v` = void, only legal for use as the result type + + It throws if an invalid signature letter is used. + + Jaccwabyt-format signatures support some additional letters which + have no special meaning here but (in this context) act as aliases + for other letters: + + - `s`, `P`: same as `p` + + Sidebar: this code is developed together with Jaccwabyt, thus the + support for its signature format. + + The arguments may be supplied in either order: (func,sig) or + (sig,func). + */ + target.jsFuncToWasm = function f(func, sig) { + /** Attribution: adapted up from Emscripten-generated glue code, + refactored primarily for efficiency's sake, eliminating + call-local functions and superfluous temporary arrays. */ + if (!f._) f._ = { + sigTypes: Object.assign(Object.create(null), { + i: "i32", + p: __ptrIR, + P: __ptrIR, + s: __ptrIR, + j: "i64", + f: "f32", + d: "f64" + }), + typeCodes: Object.assign(Object.create(null), { + f64: 124, + f32: 125, + i64: 126, + i32: 127 + }), + uleb128Encode: (tgt, method, n) => { + if (n < 128) tgt[method](n); + else tgt[method](n % 128 | 128, n >> 7); + }, + rxJSig: /^(\w)\((\w*)\)$/, + sigParams: (sig) => { + const m = f._.rxJSig.exec(sig); + return m ? m[2] : sig.substr(1); + }, + letterType: (x) => f._.sigTypes[x] || toss("Invalid signature letter:", x), + pushSigType: (dest, letter) => dest.push(f._.typeCodes[f._.letterType(letter)]) + }; + if ("string" === typeof func) { + const x = sig; + sig = func; + func = x; + } + const _ = f._; + const sigParams = _.sigParams(sig); + const wasmCode = [1, 96]; + _.uleb128Encode(wasmCode, "push", sigParams.length); + for (const x of sigParams) _.pushSigType(wasmCode, x); + if ("v" === sig[0]) wasmCode.push(0); + else { + wasmCode.push(1); + _.pushSigType(wasmCode, sig[0]); + } + _.uleb128Encode(wasmCode, "unshift", wasmCode.length); + wasmCode.unshift(0, 97, 115, 109, 1, 0, 0, 0, 1); + wasmCode.push(2, 7, 1, 1, 101, 1, 102, 0, 0, 7, 5, 1, 1, 102, 0, 0); + return new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(wasmCode)), { e: { f: func } }).exports["f"]; + }; + /** + Documented as target.installFunction() except for the 3rd + argument: if truthy, the newly-created function pointer + is stashed in the current scoped-alloc scope and will be + cleaned up at the matching scopedAllocPop(), else it + is not stashed there. + */ + const __installFunction = function f(func, sig, scoped) { + if (scoped && !cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); + if ("string" === typeof func) { + const x = sig; + sig = func; + func = x; + } + if ("string" !== typeof sig || !(func instanceof Function)) toss("Invalid arguments: expecting (function,signature) or (signature,function)."); + const ft = target.functionTable(); + const oldLen = __asPtrType(ft.length); + let ptr; + while (ptr = cache.freeFuncIndexes.pop()) if (ft.get(ptr)) { + ptr = null; + continue; + } else break; + if (!ptr) { + ptr = __asPtrType(oldLen); + ft.grow(__asPtrType(1)); + } + try { + ft.set(ptr, func); + if (scoped) cache.scopedAlloc.pushPtr(ptr); + return ptr; + } catch (e) { + if (!(e instanceof TypeError)) { + if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); + throw e; + } + } + try { + const fptr = target.jsFuncToWasm(func, sig); + ft.set(ptr, fptr); + if (scoped) cache.scopedAlloc.pushPtr(ptr); + } catch (e) { + if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); + throw e; + } + return ptr; + }; + /** + Expects a JS function and signature, exactly as for + this.jsFuncToWasm(). It uses that function to create a + WASM-exported function, installs that function to the next + available slot of this.functionTable(), and returns the + function's index in that table (which acts as a pointer to that + function). The returned pointer can be passed to + uninstallFunction() to uninstall it and free up the table slot + for reuse. + + If passed (string,function) arguments then it treats the first + argument as the signature and second as the function. + + As a special case, if the passed-in function is a WASM-exported + function then the signature argument is ignored and func is + installed as-is, without requiring re-compilation/re-wrapping. + + This function will propagate an exception if + WebAssembly.Table.grow() throws or this.jsFuncToWasm() throws. + The former case can happen in an Emscripten-compiled environment + when building without Emscripten's `-sALLOW_TABLE_GROWTH` flag. + + Sidebar: this function differs from Emscripten's addFunction() + _primarily_ in that it does not share that function's + undocumented behavior of reusing a function if it's passed to + addFunction() more than once, which leads to uninstallFunction() + breaking clients which do not take care to avoid that case: + + https://github.com/emscripten-core/emscripten/issues/17323 + */ + target.installFunction = (func, sig) => __installFunction(func, sig, false); + /** + Works exactly like installFunction() but requires that a + scopedAllocPush() is active and uninstalls the given function + when that alloc scope is popped via scopedAllocPop(). + This is used for implementing JS/WASM function bindings which + should only persist for the life of a call into a single + C-side function. + */ + target.scopedInstallFunction = (func, sig) => __installFunction(func, sig, true); + /** + Requires a pointer value previously returned from + this.installFunction(). Removes that function from the WASM + function table, marks its table slot as free for re-use, and + returns that function. It is illegal to call this before + installFunction() has been called and results are undefined if + ptr was not returned by that function. The returned function + may be passed back to installFunction() to reinstall it. + + To simplify certain use cases, if passed a falsy non-0 value + (noting that 0 is a valid function table index), this function + has no side effects and returns undefined. + */ + target.uninstallFunction = function(ptr) { + if (!ptr && __NullPtr !== ptr) return void 0; + const ft = target.functionTable(); + cache.freeFuncIndexes.push(ptr); + const rc = ft.get(ptr); + ft.set(ptr, null); + return rc; + }; + /** + Given a WASM heap memory address and a data type name in the form + (i8, i16, i32, i64, float (or f32), double (or f64)), this + fetches the numeric value from that address and returns it as a + number or, for the case of type='i64', a BigInt (with the caveat + BigInt will trigger an exception if this.bigIntEnabled is + falsy). Throws if given an invalid type. + + If the first argument is an array, it is treated as an array of + addresses and the result is an array of the values from each of + those address, using the same 2nd argument for determining the + value type to fetch. + + As a special case, if type ends with a `*`, it is considered to + be a pointer type and is treated as the WASM numeric type + appropriate for the pointer size (==this.ptr.ir). + + While possibly not obvious, this routine and its poke() + counterpart are how pointer-to-value _output_ parameters in + WASM-compiled C code can be interacted with: + + ``` + const ptr = alloc(4); + poke32(ptr, 0); // clear the ptr's value + aCFuncWithOutputPtrToInt32Arg(ptr); // e.g. void foo(int *x); + const result = peek32(ptr); // fetch ptr's value + dealloc(ptr); + ``` + + scopedAlloc() and friends can be used to make handling of + `ptr` safe against leaks in the case of an exception: + + ``` + let result; + const scope = scopedAllocPush(); + try{ + const ptr = scopedAlloc(4); + poke32(ptr, 0); + aCFuncWithOutputPtrArg(ptr); + result = peek32(ptr); + }finally{ + scopedAllocPop(scope); + } + ``` + + As a rule poke() must be called to set (typically zero out) the + pointer's value, else it will contain an essentially random + value. + + ACHTUNG: calling this often, e.g. in a loop, can have a noticably + painful impact on performance. Rather than doing so, use + heapForSize() to fetch the heap object and read directly from it. + + ACHTUNG #2: ptr may be a BigInt (and generally will be in 64-bit + builds) but this function must coerce it into a Number in order + to access the heap's contents. Ergo: BitInts outside of the + (extrardinarily genereous) address range exposed to browser-side + WASM may cause misbehavior. + + See also: poke() + */ + target.peek = function f(ptr, type = "i8") { + if (type.endsWith("*")) type = __ptrIR; + const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); + const list = Array.isArray(ptr) ? [] : void 0; + let rc; + do { + if (list) ptr = arguments[0].shift(); + switch (type) { + case "i1": + case "i8": + rc = c.HEAP8[Number(ptr) >> 0]; + break; + case "i16": + rc = c.HEAP16[Number(ptr) >> 1]; + break; + case "i32": + rc = c.HEAP32[Number(ptr) >> 2]; + break; + case "float": + case "f32": + rc = c.HEAP32F[Number(ptr) >> 2]; + break; + case "double": + case "f64": + rc = Number(c.HEAP64F[Number(ptr) >> 3]); + break; + case "i64": if (c.HEAP64) { + rc = __BigInt(c.HEAP64[Number(ptr) >> 3]); + break; + } + default: toss("Invalid type for peek():", type); + } + if (list) list.push(rc); + } while (list && arguments[0].length); + return list || rc; + }; + /** + The counterpart of peek(), this sets a numeric value at the given + WASM heap address, using the 3rd argument to define how many + bytes are written. Throws if given an invalid type. See peek() + for details about the `type` argument. If the 3rd argument ends + with `*` then it is treated as a pointer type and this function + behaves as if the 3rd argument were this.ptr.ir. + + If the first argument is an array, it is treated like a list + of pointers and the given value is written to each one. + + Returns `this`. (Prior to 2022-12-09 it returned this function.) + + ACHTUNG #1: see peek()'s ACHTUNG #1. + + ACHTUNG #2: see peek()'s ACHTUNG #2. + */ + target.poke = function(ptr, value, type = "i8") { + if (type.endsWith("*")) type = __ptrIR; + const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); + for (const p of Array.isArray(ptr) ? ptr : [ptr]) switch (type) { + case "i1": + case "i8": + c.HEAP8[Number(p) >> 0] = value; + continue; + case "i16": + c.HEAP16[Number(p) >> 1] = value; + continue; + case "i32": + c.HEAP32[Number(p) >> 2] = value; + continue; + case "float": + case "f32": + c.HEAP32F[Number(p) >> 2] = value; + continue; + case "double": + case "f64": + c.HEAP64F[Number(p) >> 3] = value; + continue; + case "i64": if (c.HEAP64) { + c.HEAP64[Number(p) >> 3] = __BigInt(value); + continue; + } + default: toss("Invalid type for poke(): " + type); + } + return this; + }; + /** + Convenience form of peek() intended for fetching + pointer-to-pointer values. If passed a single non-array argument + it returns the value of that one pointer address. If passed + multiple arguments, or a single array of arguments, it returns an + array of their values. + */ + target.peekPtr = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, __ptrIR); + /** + A variant of poke() intended for setting pointer-to-pointer + values. Its differences from poke() are that (1) it defaults to a + value of 0 and (2) it always writes to the pointer-sized heap + view. + */ + target.pokePtr = (ptr, value = 0) => target.poke(ptr, value, __ptrIR); + /** + Convenience form of peek() intended for fetching i8 values. If + passed a single non-array argument it returns the value of that + one pointer address. If passed multiple arguments, or a single + array of arguments, it returns an array of their values. + */ + target.peek8 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i8"); + /** + Convenience form of poke() intended for setting individual bytes. + Its difference from poke() is that it always writes to the + i8-sized heap view. + */ + target.poke8 = (ptr, value) => target.poke(ptr, value, "i8"); + /** i16 variant of peek8(). */ + target.peek16 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i16"); + /** i16 variant of poke8(). */ + target.poke16 = (ptr, value) => target.poke(ptr, value, "i16"); + /** i32 variant of peek8(). */ + target.peek32 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i32"); + /** i32 variant of poke8(). */ + target.poke32 = (ptr, value) => target.poke(ptr, value, "i32"); + /** i64 variant of peek8(). Will throw if this build is not + configured for BigInt support. */ + target.peek64 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i64"); + /** i64 variant of poke8(). Will throw if this build is not + configured for BigInt support. Note that this returns + a BigInt-type value, not a Number-type value. */ + target.poke64 = (ptr, value) => target.poke(ptr, value, "i64"); + /** f32 variant of peek8(). */ + target.peek32f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f32"); + /** f32 variant of poke8(). */ + target.poke32f = (ptr, value) => target.poke(ptr, value, "f32"); + /** f64 variant of peek8(). */ + target.peek64f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f64"); + /** f64 variant of poke8(). */ + target.poke64f = (ptr, value) => target.poke(ptr, value, "f64"); + /** Deprecated alias for getMemValue() */ + target.getMemValue = target.peek; + /** Deprecated alias for peekPtr() */ + target.getPtrValue = target.peekPtr; + /** Deprecated alias for poke() */ + target.setMemValue = target.poke; + /** Deprecated alias for pokePtr() */ + target.setPtrValue = target.pokePtr; + /** + Returns true if the given value appears to be legal for use as + a WASM pointer value. Its _range_ of values is not (cannot be) + validated except to ensure that it is a 32-bit integer with a + value of 0 or greater. Likewise, it cannot verify whether the + value actually refers to allocated memory in the WASM heap. + + Whether or not null or undefined are legal are context-dependent. + They generally are legal but this function does not treat them as + such because they're not strictly legal for passing as-is as WASM + integer arguments. + */ + target.isPtr32 = (ptr) => "number" === typeof ptr && ptr >= 0 && ptr === (ptr | 0); + /** 64-bit counterpart of isPtr32() and falls back to that function + if ptr is not a BigInt. */ + target.isPtr64 = (ptr) => "bigint" === typeof ptr ? ptr >= 0 : target.isPtr32(ptr); + /** + isPtr() is an alias for isPtr32() or isPtr64(), depending on the + value of target.ptr.size. + */ + target.isPtr = 4 === __ptrSize ? target.isPtr32 : target.isPtr64; + /** + Expects ptr to be a pointer into the WASM heap memory which + refers to a NUL-terminated C-style string encoded as UTF-8. + Returns the length, in bytes, of the string, as for `strlen(3)`. + As a special case, if !ptr or if it's not a pointer then it + returns `null`. Throws if ptr is out of range for + target.heap8u(). + */ + target.cstrlen = function(ptr) { + if (!ptr || !target.isPtr(ptr)) return null; + ptr = Number(ptr); + const h = heapWrappers().HEAP8U; + let pos = ptr; + for (; h[pos] !== 0; ++pos); + return pos - ptr; + }; + /** Internal helper to use in operations which need to distinguish + between TypedArrays which are backed by a SharedArrayBuffer + from those which are not. */ + const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; + /** Returns true if the given TypedArray object is backed by a + SharedArrayBuffer, else false. */ + const isSharedTypedArray = (aTypedArray) => aTypedArray.buffer instanceof __SAB; + target.isSharedTypedArray = isSharedTypedArray; + /** + Returns either aTypedArray.slice(begin,end) (if + aTypedArray.buffer is a SharedArrayBuffer) or + aTypedArray.subarray(begin,end) (if it's not). + + This distinction is important for APIs which don't like to + work on SABs, e.g. TextDecoder, and possibly for our + own APIs which work on memory ranges which "might" be + modified by other threads while they're working. + + begin and end may be of type Number or (in 64-bit builds) BigInt + (which get coerced to Numbers). + */ + const typedArrayPart = (aTypedArray, begin, end) => { + if (8 === __ptrSize) { + if ("bigint" === typeof begin) begin = Number(begin); + if ("bigint" === typeof end) end = Number(end); + } + return isSharedTypedArray(aTypedArray) ? aTypedArray.slice(begin, end) : aTypedArray.subarray(begin, end); + }; + target.typedArrayPart = typedArrayPart; + /** + Uses TextDecoder to decode the given half-open range of the given + TypedArray to a string. This differs from a simple call to + TextDecoder in that it accounts for whether the first argument is + backed by a SharedArrayBuffer or not, and can work more + efficiently if it's not (TextDecoder refuses to act upon an SAB). + + If begin/end are not provided or are falsy then each defaults to + the start/end of the array. + */ + const typedArrayToString = (typedArray, begin, end) => cache.utf8Decoder.decode(typedArrayPart(typedArray, begin, end)); + target.typedArrayToString = typedArrayToString; + /** + Expects ptr to be a pointer into the WASM heap memory which + refers to a NUL-terminated C-style string encoded as UTF-8. This + function counts its byte length using cstrlen() then returns a + JS-format string representing its contents. As a special case, if + ptr is falsy or not a pointer, `null` is returned. + */ + target.cstrToJs = function(ptr) { + const n = target.cstrlen(ptr); + return n ? typedArrayToString(heapWrappers().HEAP8U, Number(ptr), Number(ptr) + n) : null === n ? n : ""; + }; + /** + Given a JS string, this function returns its UTF-8 length in + bytes. Returns null if str is not a string. + */ + target.jstrlen = function(str) { + /** Attribution: derived from Emscripten's lengthBytesUTF8() */ + if ("string" !== typeof str) return null; + const n = str.length; + let len = 0; + for (let i = 0; i < n; ++i) { + let u = str.charCodeAt(i); + if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023; + if (u <= 127) ++len; + else if (u <= 2047) len += 2; + else if (u <= 65535) len += 3; + else len += 4; + } + return len; + }; + /** + Encodes the given JS string as UTF8 into the given TypedArray + tgt, starting at the given offset and writing, at most, maxBytes + bytes (including the NUL terminator if addNul is true, else no + NUL is added). If it writes any bytes at all and addNul is true, + it always NUL-terminates the output, even if doing so means that + the NUL byte is all that it writes. + + If maxBytes is negative (the default) then it is treated as the + remaining length of tgt, starting at the given offset. + + If writing the last character would surpass the maxBytes count + because the character is multi-byte, that character will not be + written (as opposed to writing a truncated multi-byte character). + This can lead to it writing as many as 3 fewer bytes than + maxBytes specifies. + + Returns the number of bytes written to the target, _including_ + the NUL terminator (if any). If it returns 0, it wrote nothing at + all, which can happen if: + + - str is empty and addNul is false. + - offset < 0. + - maxBytes == 0. + - maxBytes is less than the byte length of a multi-byte str[0]. + + Throws if tgt is not an Int8Array or Uint8Array. + + Design notes: + + - In C's strcpy(), the destination pointer is the first + argument. That is not the case here primarily because the 3rd+ + arguments are all referring to the destination, so it seems to + make sense to have them grouped with it. + + - Emscripten's counterpart of this function (stringToUTF8Array()) + returns the number of bytes written sans NUL terminator. That + is, however, ambiguous: str.length===0 or maxBytes===(0 or 1) + all cause 0 to be returned. + */ + target.jstrcpy = function(jstr, tgt, offset = 0, maxBytes = -1, addNul = true) { + /** Attribution: the encoding bits are taken from Emscripten's + stringToUTF8Array(). */ + if (!tgt || !(tgt instanceof Int8Array) && !(tgt instanceof Uint8Array)) toss("jstrcpy() target must be an Int8Array or Uint8Array."); + maxBytes = Number(maxBytes); + offset = Number(offset); + if (maxBytes < 0) maxBytes = tgt.length - offset; + if (!(maxBytes > 0) || !(offset >= 0)) return 0; + let i = 0, max = jstr.length; + const begin = offset, end = offset + maxBytes - (addNul ? 1 : 0); + for (; i < max && offset < end; ++i) { + let u = jstr.charCodeAt(i); + if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | jstr.charCodeAt(++i) & 1023; + if (u <= 127) { + if (offset >= end) break; + tgt[offset++] = u; + } else if (u <= 2047) { + if (offset + 1 >= end) break; + tgt[offset++] = 192 | u >> 6; + tgt[offset++] = 128 | u & 63; + } else if (u <= 65535) { + if (offset + 2 >= end) break; + tgt[offset++] = 224 | u >> 12; + tgt[offset++] = 128 | u >> 6 & 63; + tgt[offset++] = 128 | u & 63; + } else { + if (offset + 3 >= end) break; + tgt[offset++] = 240 | u >> 18; + tgt[offset++] = 128 | u >> 12 & 63; + tgt[offset++] = 128 | u >> 6 & 63; + tgt[offset++] = 128 | u & 63; + } + } + if (addNul) tgt[offset++] = 0; + return offset - begin; + }; + /** + Works similarly to C's strncpy(), copying, at most, n bytes (not + characters) from srcPtr to tgtPtr. It copies until n bytes have + been copied or a 0 byte is reached in src. _Unlike_ strncpy(), it + returns the number of bytes it assigns in tgtPtr, _including_ the + NUL byte (if any). If n is reached before a NUL byte in srcPtr, + tgtPtr will _not_ be NULL-terminated. If a NUL byte is reached + before n bytes are copied, tgtPtr will be NUL-terminated. + + If n is negative, cstrlen(srcPtr)+1 is used to calculate it, the + +1 being for the NUL byte. + + Throws if tgtPtr or srcPtr are falsy. Results are undefined if: + + - either is not a pointer into the WASM heap or + + - srcPtr is not NUL-terminated AND n is less than srcPtr's + logical length. + + ACHTUNG: it is possible to copy partial multi-byte characters + this way, and converting such strings back to JS strings will + have undefined results. + */ + target.cstrncpy = function(tgtPtr, srcPtr, n) { + if (!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings."); + if (n < 0) n = target.cstrlen(strPtr) + 1; + else if (!(n > 0)) return 0; + const heap = target.heap8u(); + let i = 0, ch; + const tgtNumber = Number(tgtPtr), srcNumber = Number(srcPtr); + for (; i < n && (ch = heap[srcNumber + i]); ++i) heap[tgtNumber + i] = ch; + if (i < n) heap[tgtNumber + i++] = 0; + return i; + }; + /** + For the given JS string, returns a Uint8Array of its contents + encoded as UTF-8. If addNul is true, the returned array will have + a trailing 0 entry, else it will not. + */ + target.jstrToUintArray = (str, addNul = false) => { + return cache.utf8Encoder.encode(addNul ? str + "\0" : str); + }; + const __affirmAlloc = (obj, funcName) => { + if (!(obj.alloc instanceof Function) || !(obj.dealloc instanceof Function)) toss("Object is missing alloc() and/or dealloc() function(s)", "required by", funcName + "()."); + }; + const __allocCStr = function(jstr, returnWithLength, allocator, funcName) { + __affirmAlloc(target, funcName); + if ("string" !== typeof jstr) return null; + const u = cache.utf8Encoder.encode(jstr), ptr = allocator(u.length + 1); + let toFree = ptr; + try { + const heap = heapWrappers().HEAP8U; + heap.set(u, Number(ptr)); + heap[__ptrAdd(ptr, u.length)] = 0; + toFree = __NullPtr; + return returnWithLength ? [ptr, u.length] : ptr; + } finally { + if (toFree) target.dealloc(toFree); + } + }; + /** + Uses target.alloc() to allocate enough memory for jstrlen(jstr)+1 + bytes of memory, copies jstr to that memory using jstrcpy(), + NUL-terminates it, and returns the pointer to that C-string. + Ownership of the pointer is transfered to the caller, who must + eventually pass the pointer to dealloc() to free it. + + If passed a truthy 2nd argument then its return semantics change: + it returns [ptr,n], where ptr is the C-string's pointer and n is + its cstrlen(). + + Throws if `target.alloc` or `target.dealloc` are not functions. + */ + target.allocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.alloc, "allocCString()"); + /** + Starts an "allocation scope." All allocations made using + scopedAlloc() are recorded in this scope and are freed when the + value returned from this function is passed to + scopedAllocPop(). + + This family of functions requires that the API's object have both + `alloc()` and `dealloc()` methods, else this function will throw. + + Intended usage: + + ``` + const scope = scopedAllocPush(); + try { + const ptr1 = scopedAlloc(100); + const ptr2 = scopedAlloc(200); + const ptr3 = scopedAlloc(300); + ... + // Note that only allocations made via scopedAlloc() + // are managed by this allocation scope. + }finally{ + scopedAllocPop(scope); + } + ``` + + The value returned by this function must be treated as opaque by + the caller, suitable _only_ for passing to scopedAllocPop(). + Its type and value are not part of this function's API and may + change in any given version of this code. + + `scopedAlloc.level` can be used to determine how many scoped + alloc levels are currently active. + */ + target.scopedAllocPush = function() { + __affirmAlloc(target, "scopedAllocPush"); + const a = []; + cache.scopedAlloc.push(a); + return a; + }; + /** + Cleans up all allocations made using scopedAlloc() in the context + of the given opaque state object, which must be a value returned + by scopedAllocPush(). See that function for an example of how to + use this function. It also uninstalls any WASM functions + installed with scopedInstallFunction(). + + Though scoped allocations are managed like a stack, this API + behaves properly if allocation scopes are popped in an order + other than the order they were pushed. The intent is that it + _always_ be used in a stack-like manner. + + If called with no arguments, it pops the most recent + scopedAllocPush() result: + + ``` + scopedAllocPush(); + try{ ... } finally { scopedAllocPop(); } + ``` + + It's generally recommended that it be passed an explicit argument + to help ensure that push/push are used in matching pairs, but in + trivial code that may be a non-issue. + */ + target.scopedAllocPop = function(state) { + __affirmAlloc(target, "scopedAllocPop"); + const n = arguments.length ? cache.scopedAlloc.indexOf(state) : cache.scopedAlloc.length - 1; + if (n < 0) toss("Invalid state object for scopedAllocPop()."); + if (0 === arguments.length) state = cache.scopedAlloc[n]; + cache.scopedAlloc.splice(n, 1); + for (let p; p = state.pop();) if (target.functionEntry(p)) target.uninstallFunction(p); + else target.dealloc(p); + }; + /** + Allocates n bytes of memory using this.alloc() and records that + fact in the state for the most recent call of scopedAllocPush(). + Ownership of the memory is given to scopedAllocPop(), which + will clean it up when it is called. The memory _must not_ be + passed to this.dealloc(). Throws if this API object is missing + the required `alloc()` or `dealloc()` functions or no scoped + alloc is active. + + See scopedAllocPush() for an example of how to use this function. + + The `level` property of this function can be queried to query how + many scoped allocation levels are currently active. + + See also: scopedAllocPtr(), scopedAllocCString() + */ + target.scopedAlloc = function(n) { + if (!cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); + const p = __asPtrType(target.alloc(n)); + return cache.scopedAlloc.pushPtr(p); + }; + Object.defineProperty(target.scopedAlloc, "level", { + configurable: false, + enumerable: false, + get: () => cache.scopedAlloc.length, + set: () => toss("The 'active' property is read-only.") + }); + /** + Works identically to allocCString() except that it allocates the + memory using scopedAlloc(). + + Will throw if no scopedAllocPush() call is active. + */ + target.scopedAllocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.scopedAlloc, "scopedAllocCString()"); + const __allocMainArgv = function(isScoped, list) { + const pList = target[isScoped ? "scopedAlloc" : "alloc"]((list.length + 1) * target.ptr.size); + let i = 0; + list.forEach((e) => { + target.pokePtr(__ptrAdd(pList, target.ptr.size * i++), target[isScoped ? "scopedAllocCString" : "allocCString"]("" + e)); + }); + target.pokePtr(__ptrAdd(pList, target.ptr.size * i), 0); + return pList; + }; + /** + Creates an array, using scopedAlloc(), suitable for passing to a + C-level main() routine. The input is a collection with a length + property and a forEach() method. A block of memory + (list.length+1) entries long is allocated and each pointer-sized + block of that memory is populated with a scopedAllocCString() + conversion of the (""+value) of each element, with the exception + that the final entry is a NULL pointer. Returns a pointer to the + start of the list, suitable for passing as the 2nd argument to a + C-style main() function. + + Throws if scopedAllocPush() is not active. + + Design note: the returned array is allocated with an extra NULL + pointer entry to accommodate certain APIs, but client code which + does not need that functionality should treat the returned array + as list.length entries long. + */ + target.scopedAllocMainArgv = (list) => __allocMainArgv(true, list); + /** + Identical to scopedAllocMainArgv() but uses alloc() instead of + scopedAlloc(). + */ + target.allocMainArgv = (list) => __allocMainArgv(false, list); + /** + Expects to be given a C-style string array and its length. It + returns a JS array of strings and/or nulls: any entry in the + pArgv array which is NULL results in a null entry in the result + array. If argc is 0 then an empty array is returned. + + Results are undefined if any entry in the first argc entries of + pArgv are neither 0 (NULL) nor legal UTF-format C strings. + + To be clear, the expected C-style arguments to be passed to this + function are `(int, char **)` (optionally const-qualified). + */ + target.cArgvToJs = (argc, pArgv) => { + const list = []; + for (let i = 0; i < argc; ++i) { + const arg = target.peekPtr(__ptrAdd(pArgv, target.ptr.size * i)); + list.push(arg ? target.cstrToJs(arg) : null); + } + return list; + }; + /** + Wraps function call func() in a scopedAllocPush() and + scopedAllocPop() block, such that all calls to scopedAlloc() and + friends from within that call will have their memory freed + automatically when func() returns. If func throws or propagates + an exception, the scope is still popped, otherwise it returns the + result of calling func(). + */ + target.scopedAllocCall = function(func) { + target.scopedAllocPush(); + try { + return func(); + } finally { + target.scopedAllocPop(); + } + }; + /** Internal impl for allocPtr() and scopedAllocPtr(). */ + const __allocPtr = function(howMany, safePtrSize, method) { + __affirmAlloc(target, method); + const pIr = safePtrSize ? "i64" : __ptrIR; + let m = target[method](howMany * (safePtrSize ? 8 : __ptrSize)); + target.poke(m, 0, pIr); + if (1 === howMany) return m; + const a = [m]; + for (let i = 1; i < howMany; ++i) { + m = __ptrAdd(m, safePtrSize ? 8 : __ptrSize); + a[i] = m; + target.poke(m, 0, pIr); + } + return a; + }; + /** + Allocates one or more pointers as a single chunk of memory and + zeroes them out. + + The first argument is the number of pointers to allocate. The + second specifies whether they should use a "safe" pointer size (8 + bytes) or whether they may use the default pointer size + (typically 4 but also possibly 8). + + How the result is returned depends on its first argument: if + passed 1, it returns the allocated memory address. If passed more + than one then an array of pointer addresses is returned, which + can optionally be used with "destructuring assignment" like this: + + ``` + const [p1, p2, p3] = allocPtr(3); + ``` + + ACHTUNG: when freeing the memory, pass only the _first_ result + value to dealloc(). The others are part of the same memory chunk + and must not be freed separately. + + The reason for the 2nd argument is... + + When one of the returned pointers will refer to a 64-bit value, + e.g. a double or int64, and that value must be written or fetched, + e.g. using poke() or peek(), it is important that + the pointer in question be aligned to an 8-byte boundary or else + it will not be fetched or written properly and will corrupt or + read neighboring memory. It is only safe to pass false when the + client code is certain that it will only get/fetch 4-byte values + (or smaller). + */ + target.allocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "alloc"); + /** + Identical to allocPtr() except that it allocates using scopedAlloc() + instead of alloc(). + */ + target.scopedAllocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "scopedAlloc"); + /** + If target.exports[name] exists, it is returned, else an + exception is thrown. + */ + target.xGet = function(name) { + return target.exports[name] || toss("Cannot find exported symbol:", name); + }; + const __argcMismatch = (f, n) => toss(f + "() requires", n, "argument(s)."); + /** + Looks up a WASM-exported function named fname from + target.exports. If found, it is called, passed all remaining + arguments, and its return value is returned to xCall's caller. If + not found, an exception is thrown. This function does no + conversion of argument or return types, but see xWrap() and + xCallWrapped() for variants which do. + + If the first argument is a function is is assumed to be + a WASM-bound function and is used as-is instead of looking up + the function via xGet(). + + As a special case, if passed only 1 argument after the name and + that argument in an Array, that array's entries become the + function arguments. (This is not an ambiguous case because it's + not legal to pass an Array object to a WASM function.) + */ + target.xCall = function(fname, ...args) { + const f = fname instanceof Function ? fname : target.xGet(fname); + if (!(f instanceof Function)) toss("Exported symbol", fname, "is not a function."); + if (f.length !== args.length) __argcMismatch(f === fname ? f.name : fname, f.length); + return 2 === arguments.length && Array.isArray(arguments[1]) ? f.apply(null, arguments[1]) : f.apply(null, args); + }; + /** + State for use with xWrap(). + */ + cache.xWrap = Object.create(null); + cache.xWrap.convert = Object.create(null); + /** Map of type names to argument conversion functions. */ + cache.xWrap.convert.arg = /* @__PURE__ */ new Map(); + /** Map of type names to return result conversion functions. */ + cache.xWrap.convert.result = /* @__PURE__ */ new Map(); + /** Scope-local convenience aliases. */ + const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result; + const __xArgPtr = __asPtrType; + xArg.set("i64", __BigInt).set("i32", (i) => i | 0).set("i16", (i) => (i | 0) & 65535).set("i8", (i) => (i | 0) & 255).set("f32", (i) => Number(i).valueOf()).set("float", xArg.get("f32")).set("f64", xArg.get("f32")).set("double", xArg.get("f64")).set("int", xArg.get("i32")).set("null", (i) => i).set(null, xArg.get("null")).set("**", __xArgPtr).set("*", __xArgPtr); + xResult.set("*", __xArgPtr).set("pointer", __xArgPtr).set("number", (v) => Number(v)).set("void", (v) => void 0).set(void 0, xResult.get("void")).set("null", (v) => v).set(null, xResult.get("null")); + for (const t of [ + "i8", + "i16", + "i32", + "i64", + "int", + "f32", + "float", + "f64", + "double" + ]) { + xArg.set(t + "*", __xArgPtr); + xResult.set(t + "*", __xArgPtr); + xResult.set(t, xArg.get(t) || toss("Maintenance required: missing arg converter for", t)); + } + /** + In order for args of type string to work in various contexts in + the sqlite3 API, we need to pass them on as, variably, a C-string + or a pointer value. Thus for ARGs of type 'string' and + '*'/'pointer' we behave differently depending on whether the + argument is a string or not: + + - If v is a string, scopeAlloc() a new C-string from it and return + that temp string's pointer. + + - Else return the value from the arg adapter defined for + target.ptr.ir. + + TODO? Permit an Int8Array/Uint8Array and convert it to a string? + Would that be too much magic concentrated in one place, ready to + backfire? We handle that at the client level in sqlite3 with a + custom argument converter. + */ + const __xArgString = (v) => { + return "string" === typeof v ? target.scopedAllocCString(v) : __asPtrType(v); + }; + xArg.set("string", __xArgString).set("utf8", __xArgString); + xResult.set("string", (i) => target.cstrToJs(i)).set("utf8", xResult.get("string")).set("string:dealloc", (i) => { + try { + return i ? target.cstrToJs(i) : null; + } finally { + target.dealloc(i); + } + }).set("utf8:dealloc", xResult.get("string:dealloc")).set("json", (i) => JSON.parse(target.cstrToJs(i))).set("json:dealloc", (i) => { + try { + return i ? JSON.parse(target.cstrToJs(i)) : null; + } finally { + target.dealloc(i); + } + }); + /** + Internal-use-only base class for FuncPtrAdapter and potentially + additional stateful argument adapter classes. + + Its main interface (convertArg()) is strictly internal, not to be + exposed to client code, as it may still need re-shaping. Only the + constructors of concrete subclasses should be exposed to clients, + and those in such a way that does not hinder internal redesign of + the convertArg() interface. + */ + const AbstractArgAdapter = class { + constructor(opt) { + this.name = opt.name || "unnamed adapter"; + } + /** + Gets called via xWrap() to "convert" v to whatever type + this specific class supports. + + argIndex is the argv index of _this_ argument in the + being-xWrap()'d call. argv is the current argument list + undergoing xWrap() argument conversion. argv entries to the + left of argIndex will have already undergone transformation and + those to the right will not have (they will have the values the + client-level code passed in, awaiting conversion). The RHS + indexes must never be relied upon for anything because their + types are indeterminate, whereas the LHS values will be + WASM-compatible values by the time this is called. + + The reason for the argv and argIndex arguments is that we + frequently need more context than v for a specific conversion, + and that context invariably lies in the LHS arguments of v. + Examples of how this is useful can be found in FuncPtrAdapter. + */ + convertArg(v, argv, argIndex) { + toss("AbstractArgAdapter must be subclassed."); + } + }; + /** + This type is recognized by xWrap() as a proxy for converting a JS + function to a C-side function, either permanently, for the + duration of a single call into the C layer, or semi-contextual, + where it may keep track of a single binding for a given context + and uninstall the binding if it's replaced. + + The constructor requires an options object with these properties: + + - name (optional): string describing the function binding. This + is solely for debugging and error-reporting purposes. If not + provided, an empty string is assumed. + + - signature: a function signature string compatible with + jsFuncToWasm(). + + - bindScope (string): one of ('transient', 'context', + 'singleton', 'permanent'). Bind scopes are: + + - 'transient': it will convert JS functions to WASM only for + the duration of the xWrap()'d function call, using + scopedInstallFunction(). Before that call returns, the + WASM-side binding will be uninstalled. + + - 'singleton': holds one function-pointer binding for this + instance. If it's called with a different function pointer, + it uninstalls the previous one after converting the new + value. This is only useful for use with "global" functions + which do not rely on any state other than this function + pointer. If the being-converted function pointer is intended + to be mapped to some sort of state object (e.g. an + `sqlite3*`) then "context" (see below) is the proper mode. + + - 'context': similar to singleton mode but for a given + "context", where the context is a key provided by the user + and possibly dependent on a small amount of call-time + context. This mode is the default if bindScope is _not_ set + but a property named contextKey (described below) is. + + - 'permanent': the function is installed and left there + forever. There is no way to recover its pointer address + later on for cleanup purposes. i.e. it effectively leaks. + + - callProxy (function): if set, this must be a function which + will act as a proxy for any "converted" JS function. It is + passed the being-converted function value and must return + either that function or a function which acts on its + behalf. The returned function will be the one which gets + installed into the WASM function table. The proxy must perform + any required argument conversion (it will be called from C + code, so will receive C-format arguments) before passing them + on to the being-converted function. Whether or not the proxy + itself must return a value depends on the context. If it does, + it must be a WASM-friendly value, as it will be returning from + a call made from WASM code. + + - contextKey (function): is only used if bindScope is 'context' + or if bindScope is not set and this function is, in which case + a bindScope of 'context' is assumed. This function gets bound + to this object, so its "this" is this object. It gets passed + (argv,argIndex), where argIndex is the index of _this_ function + in its _wrapping_ function's arguments, and argv is the + _current_ still-being-xWrap()-processed args array. (Got all + that?) When thisFunc(argv,argIndex) is called by xWrap(), all + arguments in argv to the left of argIndex will have been + processed by xWrap() by the time this is called. argv[argIndex] + will be the value the user passed in to the xWrap()'d function + for the argument this FuncPtrAdapter is mapped to. Arguments to + the right of argv[argIndex] will not yet have been converted + before this is called. The function must return a key which + uniquely identifies this function mapping context for _this_ + FuncPtrAdapter instance (other instances are not considered), + taking into account that C functions often take some sort of + state object as one or more of their arguments. As an example, + if the xWrap()'d function takes `(int,T*,functionPtr,X*)` then + this FuncPtrAdapter instance is argv[2], and contextKey(argv,2) + might return 'T@'+argv[1], or even just argv[1]. Note, + however, that the (`X*`) argument will not yet have been + processed by the time this is called and should not be used as + part of that key because its pre-conversion data type might be + unpredictable. Similarly, care must be taken with C-string-type + arguments: those to the left in argv will, when this is called, + be WASM pointers, whereas those to the right might (and likely + do) have another data type. When using C-strings in keys, never + use their pointers in the key because most C-strings in this + constellation are transient. Conversely, the pointer address + makes an ideal key for longer-lived native pointer types. + + Yes, that ^^^ is quite awkward, but it's what we have. In + context, as it were, it actually makes some sense, but one must + look under its hook a bit to understand why it's shaped the + way it is. + + The constructor only saves the above state for later, and does + not actually bind any functions. The conversion, if any, is + performed when its convertArg() method is called via xWrap(). + + Shortcomings: + + - These "reverse" bindings, i.e. calling into a JS-defined + function from a WASM-defined function (the generated proxy + wrapper), lack all type conversion support. That means, for + example, that... + + - Function pointers which include C-string arguments may still + need a level of hand-written wrappers around them, depending on + how they're used, in order to provide the client with JS + strings. Alternately, clients will need to perform such + conversions on their own, e.g. using cstrToJs(). The purpose of + the callProxy() method is to account for such cases. + */ + xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter { + constructor(opt) { + super(opt); + if (xArg.FuncPtrAdapter.warnOnUse) console.warn("xArg.FuncPtrAdapter is an internal-only API", "and is not intended to be invoked from", "client-level code. Invoked with:", opt); + this.name = opt.name || "unnamed"; + this.signature = opt.signature; + if (opt.contextKey instanceof Function) { + this.contextKey = opt.contextKey; + if (!opt.bindScope) opt.bindScope = "context"; + } + this.bindScope = opt.bindScope || toss("FuncPtrAdapter options requires a bindScope (explicit or implied)."); + if (FuncPtrAdapter.bindScopes.indexOf(opt.bindScope) < 0) toss("Invalid options.bindScope (" + opt.bindMod + ") for FuncPtrAdapter. Expecting one of: (" + FuncPtrAdapter.bindScopes.join(", ") + ")"); + this.isTransient = "transient" === this.bindScope; + this.isContext = "context" === this.bindScope; + this.isPermanent = "permanent" === this.bindScope; + this.singleton = "singleton" === this.bindScope ? [] : void 0; + this.callProxy = opt.callProxy instanceof Function ? opt.callProxy : void 0; + } + /** + The static class members are defined outside of the class to + work around an emcc toolchain build problem: one of the tools + in emsdk v3.1.42 does not support the static keyword. + */ + contextKey(argv, argIndex) { + return this; + } + /** + Returns this object's mapping for the given context key, in the + form of an an array, creating the mapping if needed. The key + may be anything suitable for use in a Map. + + The returned array is intended to be used as a pair of + [JSValue, WasmFuncPtr], where the first element is one passed + to this.convertArg() and the second is its WASM form. + */ + contextMap(key) { + const cm = this.__cmap || (this.__cmap = /* @__PURE__ */ new Map()); + let rc = cm.get(key); + if (void 0 === rc) cm.set(key, rc = []); + return rc; + } + /** + Gets called via xWrap() to "convert" v to a WASM-bound function + pointer. If v is one of (a WASM pointer, null, undefined) then + (v||0) is returned and any earlier function installed by this + mapping _might_, depending on how it's bound, be uninstalled. + If v is not one of those types, it must be a Function, for + which this method creates (if needed) a WASM function binding + and returns the WASM pointer to that binding. + + If this instance is not in 'transient' mode, it will remember + the binding for at least the next call, to avoid recreating the + function binding unnecessarily. + + If it's passed a pointer(ish) value for v, it assumes it's a + WASM function pointer and does _not_ perform any function + binding, so this object's bindMode is irrelevant/ignored for + such cases. + + See the parent class's convertArg() docs for details on what + exactly the 2nd and 3rd arguments are. + */ + convertArg(v, argv, argIndex) { + let pair = this.singleton; + if (!pair && this.isContext) pair = this.contextMap(this.contextKey(argv, argIndex)); + if (pair && 2 === pair.length && pair[0] === v) return pair[1]; + if (v instanceof Function) { + if (this.callProxy) v = this.callProxy(v); + const fp = __installFunction(v, this.signature, this.isTransient); + if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this, this.contextKey(argv, argIndex), "@" + fp, v); + if (pair) { + if (pair[1]) { + if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); + try { + cache.scopedAlloc.pushPtr(pair[1]); + } catch (e) {} + } + pair[0] = arguments[0] || __NullPtr; + pair[1] = fp; + } + return fp; + } else if (target.isPtr(v) || null === v || void 0 === v) { + if (pair && pair[1] && pair[1] !== v) { + if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); + try { + cache.scopedAlloc.pushPtr(pair[1]); + } catch (e) {} + pair[0] = pair[1] = v || __NullPtr; + } + return v || __NullPtr; + } else throw new TypeError("Invalid FuncPtrAdapter argument type. Expecting a function pointer or a " + (this.name ? this.name + " " : "") + "function matching signature " + this.signature + "."); + } + }; + /** If true, the constructor emits a warning. The intent is that + this be set to true after bootstrapping of the higher-level + client library is complete, to warn downstream clients that + they shouldn't be relying on this implementation detail which + does not have a stable interface. */ + xArg.FuncPtrAdapter.warnOnUse = false; + /** If true, convertArg() will call FuncPtrAdapter.debugOut() when + it (un)installs a function binding to/from WASM. Note that + deinstallation of bindScope=transient bindings happens via + scopedAllocPop() so will not be output. */ + xArg.FuncPtrAdapter.debugFuncInstall = false; + /** Function used for debug output. */ + xArg.FuncPtrAdapter.debugOut = console.debug.bind(console); + /** + List of legal values for the FuncPtrAdapter bindScope config + option. + */ + xArg.FuncPtrAdapter.bindScopes = [ + "transient", + "context", + "singleton", + "permanent" + ]; + /** Throws if xArg.get(t) returns falsy. */ + const __xArgAdapterCheck = (t) => xArg.get(t) || toss("Argument adapter not found:", t); + /** Throws if xResult.get(t) returns falsy. */ + const __xResultAdapterCheck = (t) => xResult.get(t) || toss("Result adapter not found:", t); + /** + Fetches the xWrap() argument adapter mapped to t, calls it, + passing in all remaining arguments, and returns the result. + Throws if t is not mapped to an argument converter. + */ + cache.xWrap.convertArg = (t, ...args) => __xArgAdapterCheck(t)(...args); + /** + Identical to convertArg() except that it does not perform + an is-defined check on the mapping to t before invoking it. + */ + cache.xWrap.convertArgNoCheck = (t, ...args) => xArg.get(t)(...args); + /** + Fetches the xWrap() result adapter mapped to t, calls it, passing + it v, and returns the result. Throws if t is not mapped to an + argument converter. + */ + cache.xWrap.convertResult = (t, v) => null === t ? v : t ? __xResultAdapterCheck(t)(v) : void 0; + /** + Identical to convertResult() except that it does not perform an + is-defined check on the mapping to t before invoking it. + */ + cache.xWrap.convertResultNoCheck = (t, v) => null === t ? v : t ? xResult.get(t)(v) : void 0; + /** + Creates a wrapper for another function which converts the arguments + of the wrapper to argument types accepted by the wrapped function, + then converts the wrapped function's result to another form + for the wrapper. + + The first argument must be one of: + + - A JavaScript function. + - The name of a WASM-exported function. xGet() is used to fetch + the exported function, which throws if it's not found. + - A pointer into the indirect function table. e.g. a pointer + returned from target.installFunction(). + + It returns either the passed-in function or a wrapper for that + function which converts the JS-side argument types into WASM-side + types and converts the result type. + + The second argument, `resultType`, describes the conversion for + the wrapped functions result. A literal `null` or the string + `'null'` both mean to return the original function's value as-is + (mnemonic: there is "null" conversion going on). Literal + `undefined` or the string `"void"` both mean to ignore the + function's result and return `undefined`. Aside from those two + special cases, the `resultType` value may be one of the values + described below or any mapping installed by the client using + xWrap.resultAdapter(). + + If passed 3 arguments and the final one is an array, that array + must contain a list of type names (see below) for adapting the + arguments from JS to WASM. If passed 2 arguments, more than 3, + or the 3rd is not an array, all arguments after the 2nd (if any) + are treated as type names. i.e.: + + ``` + xWrap('funcname', 'i32', 'string', 'f64'); + // is equivalent to: + xWrap('funcname', 'i32', ['string', 'f64']); + ``` + + This function enforces that the given list of arguments has the + same arity as the being-wrapped function (as defined by its + `length` property) and it will throw if that is not the case. + Similarly, the created wrapper will throw if passed a differing + argument count. The intent of that strictness is to help catch + coding errors in using JS-bound WASM functions earlier rather + than laer. + + Type names are symbolic names which map the arguments to an + adapter function to convert, if needed, the value before passing + it on to WASM or to convert a return result from WASM. The list + of pre-defined names: + + - `i8`, `i16`, `i32` (args and results): all integer conversions + which convert their argument to an integer and truncate it to + the given bit length. + + - `*`, `**`, and `pointer` (args): are assumed to be WASM pointer + values and are returned coerced to an appropriately-sized + pointer value (i32 or i64). Non-numeric values will coerce to 0 + and out-of-range values will have undefined results (just as + with any pointer misuse). + + - `*` and `pointer` (results): aliases for the current + WASM pointer numeric type. + + - `**` (args): is simply a descriptive alias for the WASM pointer + type. It's primarily intended to mark output-pointer arguments, + noting that JS's view of WASM does not distinguish between + pointers and pointers-to-pointers, so all such interpretation + of `**`, as distinct from `*`, necessarily happens at the + client level. + + - `NumType*` (args): a type name in this form, where T is + the name of a numeric mapping, e.g. 'int16' or 'double', + is treated like `*`. + + - `i64` (args and results): passes the value to BigInt() to + convert it to an int64. This conversion will if bigIntEnabled + is falsy. + + - `f32` (`float`), `f64` (`double`) (args and results): pass + their argument to Number(). i.e. the adapter does not currently + distinguish between the two types of floating-point numbers. + + - `number` (results): converts the result to a JS Number using + Number(theValue). This is for result conversions only, as it's + not possible to generically know which type of number to + convert arguments to. + + Non-numeric conversions include: + + - `null` literal or `"null"` string (args and results): perform + no translation and pass the arg on as-is. This is primarily + useful for results but may have a use or two for arguments. + + - `string` or `utf8` (args): has two different semantics in order + to accommodate various uses of certain C APIs + (e.g. output-style strings)... + + - If the arg is a JS string, it creates a _temporary_ + UTF-8-encoded C-string to pass to the exported function, + cleaning it up before the wrapper returns. If a long-lived + C-string pointer is required, that requires client-side code + to create the string then pass its pointer to the function. + + - Else the arg is assumed to be a pointer to a string the + client has already allocated and it's passed on as + a WASM pointer. + + - `string` or `utf8` (results): treats the result value as a + const C-string, encoded as UTF-8, copies it to a JS string, + and returns that JS string. + + - `string:dealloc` or `utf8:dealloc` (results): treats the result + value as a non-const UTF-8 C-string, ownership of which has + just been transfered to the caller. It copies the C-string to a + JS string, frees the C-string using dealloc(), and returns the + JS string. If such a result value is NULL, the JS result is + `null`. Achtung: when using an API which returns results from a + specific allocator, e.g. `my_malloc()`, this conversion _is not + legal_. Instead, an equivalent conversion which uses the + appropriate deallocator is required. For example: + + ```js + target.xWrap.resultAdapter('string:my_free',(i)=>{ + try { return i ? target.cstrToJs(i) : null; } + finally{ target.exports.my_free(i); } + }; + ``` + + - `json` (results): treats the result as a const C-string and + returns the result of passing the converted-to-JS string to + JSON.parse(). Returns `null` if the C-string is a NULL pointer. + + - `json:dealloc` (results): works exactly like `string:dealloc` but + returns the same thing as the `json` adapter. Note the + warning in `string:dealloc` regarding matching allocators and + deallocators. + + The type names for results and arguments are validated when + xWrap() is called and any unknown names will trigger an + exception. + + Clients may map their own result and argument adapters using + xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all + type conversions are valid for both arguments _and_ result types + as they often have different memory ownership requirements. + + Design note: the ability to pass in a JS function as the first + argument is of relatively limited use, primarily for testing + argument and result converters. JS functions, by and large, will + not want to deal with C-type arguments. + + TODOs: + + - Figure out how/whether we can (semi-)transparently handle + pointer-type _output_ arguments. Those currently require + explicit handling by allocating pointers, assigning them before + the call using poke(), and fetching them with + peek() after the call. We may be able to automate some + or all of that. + + - Figure out whether it makes sense to extend the arg adapter + interface such that each arg adapter gets an array containing + the results of the previous arguments in the current call. That + might allow some interesting type-conversion feature. Use case: + handling of the final argument to sqlite3_prepare_v2() depends + on the type (pointer vs JS string) of its 2nd + argument. Currently that distinction requires hand-writing a + wrapper for that function. That case is unusual enough that + abstracting it into this API (and taking on the associated + costs) may well not make good sense. + */ + target.xWrap = function callee(fArg, resultType, ...argTypes) { + if (3 === arguments.length && Array.isArray(arguments[2])) argTypes = arguments[2]; + if (target.isPtr(fArg)) fArg = target.functionEntry(fArg) || toss("Function pointer not found in WASM function table."); + const fIsFunc = fArg instanceof Function; + const xf = fIsFunc ? fArg : target.xGet(fArg); + if (fIsFunc) fArg = xf.name || "unnamed function"; + if (argTypes.length !== xf.length) __argcMismatch(fArg, xf.length); + if (0 === xf.length && (null === resultType || "null" === resultType)) return xf; + __xResultAdapterCheck(resultType); + for (const t of argTypes) if (t instanceof AbstractArgAdapter) xArg.set(t, (...args) => t.convertArg(...args)); + else __xArgAdapterCheck(t); + const cxw = cache.xWrap; + if (0 === xf.length) return (...args) => args.length ? __argcMismatch(fArg, xf.length) : cxw.convertResult(resultType, xf.call(null)); + return function(...args) { + if (args.length !== xf.length) __argcMismatch(fArg, xf.length); + const scope = target.scopedAllocPush(); + try { + let i = 0; + if (callee.debug) console.debug("xWrap() preparing: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); + for (; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], args, i); + if (callee.debug) console.debug("xWrap() calling: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); + return cxw.convertResultNoCheck(resultType, xf.apply(null, args)); + } finally { + target.scopedAllocPop(scope); + } + }; + }; + /** + Internal impl for xWrap.resultAdapter() and argAdapter(). + + func = one of xWrap.resultAdapter or xWrap.argAdapter. + + argc = the number of args in the wrapping call to this + function. + + typeName = the first arg to the wrapping function. + + adapter = the second arg to the wrapping function. + + modeName = a descriptive name of the wrapping function for + error-reporting purposes. + + xcvPart = one of xResult or xArg. + + This acts as either a getter (if 1===argc) or setter (if + 2===argc) for the given adapter. Returns func on success or + throws if (A) called with 2 args but adapter is-not-a Function or + (B) typeName is not a string or (C) argc is not one of (1, 2). + */ + const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart) { + if ("string" === typeof typeName) { + if (1 === argc) return xcvPart.get(typeName); + else if (2 === argc) { + if (!adapter) { + xcvPart.delete(typeName); + return func; + } else if (!(adapter instanceof Function)) toss(modeName, "requires a function argument."); + xcvPart.set(typeName, adapter); + return func; + } + } + toss("Invalid arguments to", modeName); + }; + /** + Gets, sets, or removes a result value adapter for use with + xWrap(). If passed only 1 argument, the adapter function for the + given type name is returned. If the second argument is explicit + falsy (as opposed to defaulted), the adapter named by the first + argument is removed. If the 2nd argument is not falsy, it must be + a function which takes one value and returns a value appropriate + for the given type name. The adapter may throw if its argument is + not of a type it can work with. This function throws for invalid + arguments. + + Example: + + ``` + xWrap.resultAdapter('twice',(v)=>v+v); + ``` + + Result adapters MUST NOT use the scopedAlloc() family of APIs to + allocate a result value. xWrap()-generated wrappers run in the + context of scopedAllocPush() so that argument adapters can easily + convert, e.g., to C-strings, and have them cleaned up + automatically before the wrapper returns to the caller. Likewise, + if a _result_ adapter uses scoped allocation, the result will be + freed before the wrapper returns, leading to chaos and undefined + behavior. + + When called as a setter, this function returns itself. + */ + target.xWrap.resultAdapter = function f(typeName, adapter) { + return __xAdapter(f, arguments.length, typeName, adapter, "resultAdapter()", xResult); + }; + /** + Functions identically to xWrap.resultAdapter() but applies to + call argument conversions instead of result value conversions. + + xWrap()-generated wrappers perform argument conversion in the + context of a scopedAllocPush(), so any memory allocation + performed by argument adapters really, really, really should be + made using the scopedAlloc() family of functions unless + specifically necessary. For example: + + ``` + xWrap.argAdapter('my-string', function(v){ + return ('string'===typeof v) + ? myWasmObj.scopedAllocCString(v) : null; + }; + ``` + + Contrariwise, _result_ adapters _must not_ use scopedAlloc() to + allocate results because they would be freed before the + xWrap()-created wrapper returns. + + It is perfectly legitimate to use these adapters to perform + argument validation, as opposed (or in addition) to conversion. + When used that way, they should throw for invalid arguments. + */ + target.xWrap.argAdapter = function f(typeName, adapter) { + return __xAdapter(f, arguments.length, typeName, adapter, "argAdapter()", xArg); + }; + target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter; + /** + Functions like xCall() but performs argument and result type + conversions as for xWrap(). The first, second, and third + arguments are as documented for xWrap(), except that the 3rd + argument may be either a falsy value or empty array to represent + nullary functions. The 4th+ arguments are arguments for the call, + with the special case that if the 4th argument is an array, it is + used as the arguments for the call. Returns the converted result + of the call. + + This is just a thin wrapper around xWrap(). If the given function + is to be called more than once, it's more efficient to use + xWrap() to create a wrapper, then to call that wrapper as many + times as needed. For one-shot calls, however, this variant is + simpler. + */ + target.xCallWrapped = function(fArg, resultType, argTypes, ...args) { + if (Array.isArray(arguments[3])) args = arguments[3]; + return target.xWrap(fArg, resultType, argTypes || []).apply(null, args || []); + }; + /** + This function is ONLY exposed in the public API to facilitate + testing. It should not be used in application-level code, only + in test code. + + Expects to be given (typeName, value) and returns a conversion + of that value as has been registered using argAdapter(). + It throws if no adapter is found. + + ACHTUNG: the adapter may require that a scopedAllocPush() is + active and it may allocate memory within that scope. It may also + require additional arguments, depending on the type of + conversion. + */ + target.xWrap.testConvertArg = cache.xWrap.convertArg; + /** + This function is ONLY exposed in the public API to facilitate + testing. It should not be used in application-level code, only + in test code. + + Expects to be given (typeName, value) and returns a conversion + of that value as has been registered using resultAdapter(). + It throws if no adapter is found. + + ACHTUNG: the adapter may allocate memory which the caller may need + to know how to free. + */ + target.xWrap.testConvertResult = cache.xWrap.convertResult; + return target; + }; + /** + yawl (Yet Another Wasm Loader) provides very basic wasm loader. + It requires a config object: + + - `uri`: required URI of the WASM file to load. + + - `onload(loadResult)`: optional callback. Its argument is an + object described in more detail below. + + - `imports`: optional imports object for + WebAssembly.instantiate[Streaming](). The default is an empty + set of imports. If the module requires any imports, this object + must include them. + + - `wasmUtilTarget`: optional object suitable for passing to + WhWasmUtilInstaller(). If set, it gets passed to that function + before the returned promise resolves. This function sets several + properties on it before passing it on to that function (which + sets many more): + + - `module`, `instance`: the properties from the + instantiate[Streaming]() result. + + - If `instance.exports.memory` is _not_ set then it requires that + `config.imports.env.memory` be set (else it throws), and + assigns that to `wasmUtilTarget.memory`. + + - If `wasmUtilTarget.alloc` is not set and + `instance.exports.malloc` is, it installs + `wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()` + wrappers for the exports' `malloc` and `free` functions + if exports.malloc exists. + + It returns a function which, when called, initiates loading of the + module and returns a Promise. When that Promise resolves, it calls + the `config.onload` callback (if set) and passes it `(loadResult)`, + where `loadResult` is derived from the result of + WebAssembly.instantiate[Streaming](), an object in the form: + + ``` + { + module: a WebAssembly.Module, + instance: a WebAssembly.Instance, + config: the config arg to this function + } + ``` + + (The initial `then()` attached to the promise gets only that + object, and not the `config` object, thus the potential need for a + `config.onload` handler.) + + Error handling is up to the caller, who may attach a `catch()` call + to the promise. + */ + globalThis.WhWasmUtilInstaller.yawl = function yawl(config) { + "use strict"; + const wfetch = () => fetch(config.uri, { credentials: "same-origin" }); + const wui = this; + const finalThen = function(arg) { + if (config.wasmUtilTarget) { + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + const tgt = config.wasmUtilTarget; + tgt.module = arg.module; + tgt.instance = arg.instance; + if (!tgt.instance.exports.memory) + /** + WhWasmUtilInstaller requires either tgt.exports.memory + (exported from WASM) or tgt.memory (JS-provided memory + imported into WASM). + */ + tgt.memory = config?.imports?.env?.memory || toss("Missing 'memory' object!"); + if (!tgt.alloc && arg.instance.exports.malloc) { + const exports = arg.instance.exports; + tgt.alloc = function(n) { + return exports.malloc(n) || toss("Allocation of", n, "bytes failed."); + }; + tgt.dealloc = function(m) { + m && exports.free(m); + }; + } + wui(tgt); + } + arg.config = config; + if (config.onload) config.onload(arg); + return arg; + }; + return WebAssembly.instantiateStreaming ? () => WebAssembly.instantiateStreaming(wfetch(), config.imports || {}).then(finalThen) : () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, config.imports || {})).then(finalThen); + }.bind(globalThis.WhWasmUtilInstaller); + globalThis.Jaccwabyt = function StructBinderFactory(config) { + "use strict"; + /** Throws a new Error, the message of which is the concatenation + all args with a space between each. */ + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + { + let h = config.heap; + if (h instanceof WebAssembly.Memory) h = function() { + return new Uint8Array(this.buffer); + }.bind(h); + else if (!(h instanceof Function)) toss("config.heap must be WebAssembly.Memory instance or", "a function which returns one."); + config.heap = h; + } + ["alloc", "dealloc"].forEach(function(k) { + config[k] instanceof Function || toss("Config option '" + k + "' must be a function."); + }); + const SBF = StructBinderFactory, heap = config.heap, alloc = config.alloc, dealloc = config.dealloc; + config.realloc; + const log = config.log || console.debug.bind(console), memberPrefix = config.memberPrefix || "", memberSuffix = config.memberSuffix || "", BigInt = globalThis["BigInt"], BigInt64Array = globalThis["BigInt64Array"], bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array; + let ptr; + const ptrSize = config.pointerSize || config.ptrSize || ("bigint" === typeof (ptr = alloc(1)) ? 8 : 4); + const ptrIR = config.pointerIR || config.ptrIR || (4 === ptrSize ? "i32" : "i64"); + if (ptr) { + dealloc(ptr); + ptr = void 0; + } + if (ptrSize !== 4 && ptrSize !== 8) toss("Invalid pointer size:", ptrSize); + if (ptrIR !== "i32" && ptrIR !== "i64") toss("Invalid pointer representation:", ptrIR); + /** Either BigInt or, if !bigIntEnabled, a function which + throws complaining that BigInt is not enabled. */ + const __BigInt = bigIntEnabled && BigInt ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); + const __asPtrType = "i32" == ptrIR ? Number : __BigInt; + const __NullPtr = __asPtrType(0); + /** + Expects any number of numeric arguments, each one of either type + Number or BigInt. It sums them up (from an implicit starting + point of 0 or 0n) and returns them as a number of the same type + which target.asPtrType() uses. + + This is a workaround for not being able to mix Number/BigInt in + addition/subtraction expressions (which we frequently need for + calculating pointer offsets). + */ + const __ptrAdd = function(...args) { + let rc = __NullPtr; + for (let i = 0; i < args.length; ++i) rc += __asPtrType(args[i]); + return rc; + }; + const __ptrAddSelf = function(...args) { + return __ptrAdd(this.pointer, ...args); + }; + if (!SBF.debugFlags) { + SBF.__makeDebugFlags = function(deriveFrom = null) { + if (deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags; + const f = function f(flags) { + if (0 === arguments.length) return f.__flags; + if (flags < 0) { + delete f.__flags.getter; + delete f.__flags.setter; + delete f.__flags.alloc; + delete f.__flags.dealloc; + } else { + f.__flags.getter = 0 !== (1 & flags); + f.__flags.setter = 0 !== (2 & flags); + f.__flags.alloc = 0 !== (4 & flags); + f.__flags.dealloc = 0 !== (8 & flags); + } + return f._flags; + }; + Object.defineProperty(f, "__flags", { + iterable: false, + writable: false, + value: Object.create(deriveFrom) + }); + if (!deriveFrom) f(0); + return f; + }; + SBF.debugFlags = SBF.__makeDebugFlags(); + } + const isLittleEndian = true; + /** + Some terms used in the internal docs: + + StructType: a struct-wrapping class generated by this + framework. + + DEF: struct description object. + + SIG: struct member signature string. + */ + /** True if SIG s looks like a function signature, else + false. */ + const isFuncSig = (s) => "(" === s[1]; + /** True if SIG s is-a pointer-type signature. */ + const isPtrSig = (s) => "p" === s || "P" === s || "s" === s; + const isAutoPtrSig = (s) => "P" === s; + /** Returns p if SIG s is a function SIG, else returns s[0]. */ + const sigLetter = (s) => s ? isFuncSig(s) ? "p" : s[0] : void 0; + /** Returns the WASM IR form of the letter at SIG s[0]. Throws for + an unknown SIG. */ + const sigIR = function(s) { + switch (sigLetter(s)) { + case "c": + case "C": return "i8"; + case "i": return "i32"; + case "p": + case "P": + case "s": return ptrIR; + case "j": return "i64"; + case "f": return "float"; + case "d": return "double"; + } + toss("Unhandled signature IR:", s); + }; + /** Returns the WASM sizeof of the letter at SIG s[0]. Throws for an + unknown SIG. */ + const sigSize = function(s) { + switch (sigLetter(s)) { + case "c": + case "C": return 1; + case "i": return 4; + case "p": + case "P": + case "s": return ptrSize; + case "j": return 8; + case "f": return 4; + case "d": return 8; + } + toss("Unhandled signature sizeof:", s); + }; + const affirmBigIntArray = BigInt64Array ? () => true : () => toss("BigInt64Array is not available."); + /** Returns the name of a DataView getter method corresponding + to the given SIG. */ + const sigDVGetter = function(s) { + switch (sigLetter(s)) { + case "p": + case "P": + case "s": + switch (ptrSize) { + case 4: return "getInt32"; + case 8: return affirmBigIntArray() && "getBigInt64"; + } + break; + case "i": return "getInt32"; + case "c": return "getInt8"; + case "C": return "getUint8"; + case "j": return affirmBigIntArray() && "getBigInt64"; + case "f": return "getFloat32"; + case "d": return "getFloat64"; + } + toss("Unhandled DataView getter for signature:", s); + }; + /** Returns the name of a DataView setter method corresponding + to the given SIG. */ + const sigDVSetter = function(s) { + switch (sigLetter(s)) { + case "p": + case "P": + case "s": + switch (ptrSize) { + case 4: return "setInt32"; + case 8: return affirmBigIntArray() && "setBigInt64"; + } + break; + case "i": return "setInt32"; + case "c": return "setInt8"; + case "C": return "setUint8"; + case "j": return affirmBigIntArray() && "setBigInt64"; + case "f": return "setFloat32"; + case "d": return "setFloat64"; + } + toss("Unhandled DataView setter for signature:", s); + }; + /** + Returns a factory for either Number or BigInt, depending on the + given SIG. This constructor is used in property setters to coerce + the being-set value to the correct pointer size. + */ + const sigDVSetWrapper = function(s) { + switch (sigLetter(s)) { + case "i": + case "f": + case "c": + case "C": + case "d": return Number; + case "j": return __BigInt; + case "p": + case "P": + case "s": + switch (ptrSize) { + case 4: return Number; + case 8: return __BigInt; + } + break; + } + toss("Unhandled DataView set wrapper for signature:", s); + }; + /** Returns the given struct and member name in a form suitable for + debugging and error output. */ + const sPropName = (s, k) => s + "::" + k; + const __propThrowOnSet = function(structName, propName) { + return () => toss(sPropName(structName, propName), "is read-only."); + }; + /** + In order to completely hide StructBinder-bound struct pointers + from JS code, we store them in a scope-local WeakMap which maps + the struct-bound objects to an object with their metadata: + + { + .p = the native pointer, + .o = self (for an eventual reverse-mapping), + .xb = extra bytes allocated for p, + .zod = zeroOnDispose, + .ownsPointer = true if this object owns p + } + + The .p data are accessible via obj.pointer, which is gated behind + a property interceptor, but are not exposed anywhere else in the + public API. + */ + const getInstanceHandle = function f(obj, create = true) { + let ii = f.map.get(obj); + if (!ii && create) f.map.set(obj, ii = f.create(obj)); + return ii; + }; + getInstanceHandle.map = /* @__PURE__ */ new WeakMap(); + getInstanceHandle.create = (forObj) => { + return Object.assign(Object.create(null), { + o: forObj, + p: void 0, + ownsPointer: false, + zod: false, + xb: 0 + }); + }; + /** + Remove the getInstanceHandle() mapping for obj. + */ + const rmInstanceHandle = (obj) => getInstanceHandle.map.delete(obj); + const __isPtr32 = (ptr) => "number" === typeof ptr && ptr === (ptr | 0) && ptr >= 0; + const __isPtr64 = (ptr) => "bigint" === typeof ptr && ptr >= 0 || __isPtr32(ptr); + /** + isPtr() is an alias for isPtr32() or isPtr64(), depending on + ptrSize. + */ + const __isPtr = 4 === ptrSize ? __isPtr32 : __isPtr64; + const __isNonNullPtr = (v) => __isPtr(v) && v > 0; + /** Frees the obj.pointer memory (a.k.a. m), handles obj.ondispose, + and unmaps obj from its native resources. */ + const __freeStruct = function(ctor, obj, m) { + const ii = getInstanceHandle(obj, false); + if (!ii) return; + rmInstanceHandle(obj); + if (!m && !(m = ii.p)) { + console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo"); + return; + } + if (Array.isArray(obj.ondispose)) { + let x; + while (x = obj.ondispose.pop()) try { + if (x instanceof Function) x.call(obj); + else if (x instanceof StructType) x.dispose(); + else if (__isPtr(x)) dealloc(x); + } catch (e) { + console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); + } + } else if (obj.ondispose instanceof Function) try { + obj.ondispose(); + } catch (e) { + console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); + } + delete obj.ondispose; + if (ctor.debugFlags.__flags.dealloc) log("debug.dealloc:", ii.ownsPointer ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + m); + if (ii.ownsPointer) { + if (ii.zod || ctor.structInfo.zeroOnDispose) heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof + ii.xb); + dealloc(m); + } + }; + /** Returns a skeleton for a read-only, non-iterable property + * descriptor. */ + const rop0 = () => { + return { + configurable: false, + writable: false, + iterable: false + }; + }; + /** Returns a skeleton for a read-only property accessor wrapping + value v. */ + const rop = (v) => { + return { + ...rop0(), + value: v + }; + }; + /** Allocates obj's memory buffer based on the size defined in + ctor.structInfo.sizeof. */ + const __allocStruct = function f(ctor, obj, xm) { + let opt; + const checkPtr = (ptr) => { + __isNonNullPtr(ptr) || toss("Invalid pointer value", arguments[0], "for", ctor.structName, "constructor."); + }; + if (arguments.length >= 3) if (xm && "object" === typeof xm) { + opt = xm; + xm = opt?.wrap; + } else { + checkPtr(xm); + opt = { wrap: xm }; + } + else opt = {}; + const fill = !xm; + let nAlloc = 0; + let ownsPointer = false; + if (xm) { + checkPtr(xm); + ownsPointer = !!opt?.takeOwnership; + } else { + const nX = opt?.extraBytes ?? 0; + if (nX < 0 || nX !== (nX | 0)) toss("Invalid extraBytes value:", opt?.extraBytes); + nAlloc = ctor.structInfo.sizeof + nX; + xm = alloc(nAlloc) || toss("Allocation of", ctor.structName, "structure failed."); + ownsPointer = true; + } + try { + if (opt?.debugFlags) obj.debugFlags(opt.debugFlags); + if (ctor.debugFlags.__flags.alloc) log("debug.alloc:", fill ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + xm); + if (fill) heap().fill(0, Number(xm), Number(xm) + nAlloc); + const ii = getInstanceHandle(obj); + ii.p = xm; + ii.ownsPointer = ownsPointer; + ii.xb = nAlloc ? nAlloc - ctor.structInfo.sizeof : 0; + ii.zod = !!opt?.zeroOnDispose; + if (opt?.ondispose && opt.ondispose !== xm) obj.addOnDispose(opt.ondispose); + } catch (e) { + __freeStruct(ctor, obj, xm); + throw e; + } + }; + /** True if sig looks like an emscripten/jaccwabyt + type signature, else false. */ + const looksLikeASig = function f(sig) { + f.rxSig1 ??= /^[ipPsjfdcC]$/; + f.rxSig2 ??= /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/; + return f.rxSig1.test(sig) || f.rxSig2.test(sig); + }; + /** Returns a pair of adaptor maps (objects) in a length-3 + array specific to the given object. */ + const __adaptorsFor = function(who) { + let x = this.get(who); + if (!x) { + x = [ + Object.create(null), + Object.create(null), + Object.create(null) + ]; + this.set(who, x); + } + return x; + }.bind(/* @__PURE__ */ new WeakMap()); + /** Code de-duplifier for __adaptGet(), __adaptSet(), and + __adaptStruct(). */ + const __adaptor = function(who, which, key, proxy) { + const a = __adaptorsFor(who)[which]; + if (3 === arguments.length) return a[key]; + if (proxy) return a[key] = proxy; + return delete a[key]; + }; + const __adaptGet = function(key, ...args) { + return __adaptor(this, 0, key, ...args); + }; + const __affirmNotASig = function(ctx, key) { + looksLikeASig(key) && toss(ctx, "(", key, ") collides with a data type signature."); + }; + const __adaptSet = function(key, ...args) { + __affirmNotASig("Setter adaptor", key); + return __adaptor(this, 1, key, ...args); + }; + const __adaptStruct = function(key, ...args) { + __affirmNotASig("Struct adaptor", key); + return __adaptor(this, 2, key, ...args); + }; + /** + An internal counterpart of __adaptStruct(). If key is-a string, + uses __adaptor(who) to fetch the struct-adaptor entry for key, + else key is assumed to be a struct description object. If it + resolves to an object, that's returned, else an exception is + thrown. + */ + const __adaptStruct2 = function(who, key) { + const si = "string" === typeof key ? __adaptor(who, 2, key) : key; + if ("object" !== typeof si) toss("Invalid struct mapping object. Arg =", key, JSON.stringify(si)); + return si; + }; + const __memberKey = (k) => memberPrefix + k + memberSuffix; + const __memberKeyProp = rop(__memberKey); + /** + Looks up a struct member in structInfo.members. Throws if found + if tossIfNotFound is true, else returns undefined if not + found. The given name may be either the name of the + structInfo.members key (faster) or the key as modified by the + memberPrefix and memberSuffix settings. + */ + const __lookupMember = function(structInfo, memberName, tossIfNotFound = true) { + let m = structInfo.members[memberName]; + if (!m && (memberPrefix || memberSuffix)) { + for (const v of Object.values(structInfo.members)) if (v.key === memberName) { + m = v; + break; + } + if (!m && tossIfNotFound) toss(sPropName(structInfo.name || structInfo.structName, memberName), "is not a mapped struct member."); + } + return m; + }; + /** + Uses __lookupMember(obj.structInfo,memberName) to find a member, + throwing if not found. Returns its signature, either in this + framework's native format or in Emscripten format. + */ + const __memberSignature = function f(obj, memberName, emscriptenFormat = false) { + if (!f._) f._ = (x) => x.replace(/[^vipPsjrdcC]/g, "").replace(/[pPscC]/g, "i"); + const m = __lookupMember(obj.structInfo, memberName, true); + return emscriptenFormat ? f._(m.signature) : m.signature; + }; + /** Impl of X.memberKeys() for StructType and struct ctors. */ + const __structMemberKeys = rop(function() { + const a = []; + for (const k of Object.keys(this.structInfo.members)) a.push(this.memberKey(k)); + return a; + }); + const __utf8Decoder = new TextDecoder("utf-8"); + const __utf8Encoder = new TextEncoder(); + /** Internal helper to use in operations which need to distinguish + between SharedArrayBuffer heap memory and non-shared heap. */ + const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; + const __utf8Decode = function(arrayBuffer, begin, end) { + if (8 === ptrSize) { + begin = Number(begin); + end = Number(end); + } + return __utf8Decoder.decode(arrayBuffer.buffer instanceof __SAB ? arrayBuffer.slice(begin, end) : arrayBuffer.subarray(begin, end)); + }; + /** + Uses __lookupMember() to find the given obj.structInfo key. + Returns that member if it is a string, else returns false. If the + member is not found, throws if tossIfNotFound is true, else + returns false. + */ + const __memberIsString = function(obj, memberName, tossIfNotFound = false) { + const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound); + return m && 1 === m.signature.length && "s" === m.signature[0] ? m : false; + }; + /** + Given a member description object, throws if member.signature is + not valid for assigning to or interpretation as a C-style string. + It optimistically assumes that any signature of (i,p,s) is + C-string compatible. + */ + const __affirmCStringSignature = function(member) { + if ("s" === member.signature) return; + toss("Invalid member type signature for C-string value:", JSON.stringify(member)); + }; + /** + Looks up the given member in obj.structInfo. If it has a + signature of 's' then it is assumed to be a C-style UTF-8 string + and a decoded copy of the string at its address is returned. If + the signature is of any other type, it throws. If an s-type + member's address is 0, `null` is returned. + */ + const __memberToJsString = function f(obj, memberName) { + const m = __lookupMember(obj.structInfo, memberName, true); + __affirmCStringSignature(m); + const addr = obj[m.key]; + if (!addr) return null; + let pos = addr; + const mem = heap(); + for (; mem[pos] !== 0; ++pos); + return addr === pos ? "" : __utf8Decode(mem, addr, pos); + }; + /** + Adds value v to obj.ondispose, creating ondispose, + or converting it to an array, if needed. + */ + const __addOnDispose = function(obj, ...v) { + if (obj.ondispose) { + if (!Array.isArray(obj.ondispose)) obj.ondispose = [obj.ondispose]; + } else obj.ondispose = []; + obj.ondispose.push(...v); + }; + /** + Allocates a new UTF-8-encoded, NUL-terminated copy of the given + JS string and returns its address relative to heap(). If + allocation returns 0 this function throws. Ownership of the + memory is transfered to the caller, who must eventually pass it + to the configured dealloc() function. + */ + const __allocCString = function(str) { + const u = __utf8Encoder.encode(str); + const mem = alloc(u.length + 1); + if (!mem) toss("Allocation error while duplicating string:", str); + const h = heap(); + h.set(u, Number(mem)); + h[__ptrAdd(mem, u.length)] = 0; + return mem; + }; + /** + Sets the given struct member of obj to a dynamically-allocated, + UTF-8-encoded, NUL-terminated copy of str. It is up to the caller + to free any prior memory, if appropriate. The newly-allocated + string is added to obj.ondispose so will be freed when the object + is disposed. + + The given name may be either the name of the structInfo.members + key (faster) or the key as modified by the memberPrefix and + memberSuffix settings. + */ + const __setMemberCString = function(obj, memberName, str) { + const m = __lookupMember(obj.structInfo, memberName, true); + __affirmCStringSignature(m); + const mem = __allocCString(str); + obj[m.key] = mem; + __addOnDispose(obj, mem); + return obj; + }; + /** + Prototype for all StructFactory instances (the constructors + returned from StructBinder). + */ + const StructType = function StructType(structName, structInfo) { + if (arguments[2] !== rop) toss("Do not call the StructType constructor", "from client-level code."); + Object.defineProperties(this, { + structName: rop(structName), + structInfo: rop(structInfo) + }); + }; + /** + Properties inherited by struct-type-specific StructType instances + and (indirectly) concrete struct-type instances. + */ + StructType.prototype = Object.create(null, { + dispose: rop(function() { + __freeStruct(this.constructor, this); + }), + lookupMember: rop(function(memberName, tossIfNotFound = true) { + return __lookupMember(this.structInfo, memberName, tossIfNotFound); + }), + memberToJsString: rop(function(memberName) { + return __memberToJsString(this, memberName); + }), + memberIsString: rop(function(memberName, tossIfNotFound = true) { + return __memberIsString(this, memberName, tossIfNotFound); + }), + memberKey: __memberKeyProp, + memberKeys: __structMemberKeys, + memberSignature: rop(function(memberName, emscriptenFormat = false) { + return __memberSignature(this, memberName, emscriptenFormat); + }), + memoryDump: rop(function() { + const p = this.pointer; + return p ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof)) : null; + }), + extraBytes: { + configurable: false, + enumerable: false, + get: function() { + return getInstanceHandle(this, false)?.xb ?? 0; + } + }, + zeroOnDispose: { + configurable: false, + enumerable: false, + get: function() { + return getInstanceHandle(this, false)?.zod ?? !!this.structInfo.zeroOnDispose; + } + }, + pointer: { + configurable: false, + enumerable: false, + get: function() { + return getInstanceHandle(this, false)?.p; + }, + set: () => toss("Cannot assign the 'pointer' property of a struct.") + }, + setMemberCString: rop(function(memberName, str) { + return __setMemberCString(this, memberName, str); + }) + }); + Object.assign(StructType.prototype, { addOnDispose: function(...v) { + __addOnDispose(this, ...v); + return this; + } }); + /** + "Static" properties for StructType. + */ + Object.defineProperties(StructType, { + allocCString: rop(__allocCString), + isA: rop((v) => v instanceof StructType), + hasExternalPointer: rop((v) => { + const ii = getInstanceHandle(v, false); + return !!(ii?.p && !ii?.ownsPointer); + }), + memberKey: __memberKeyProp + }); + /** + If struct description object si has a getter proxy, return it (a + function), else return undefined. + */ + const memberGetterProxy = function(si) { + return si.get || (si.adaptGet ? StructBinder.adaptGet(si.adaptGet) : void 0); + }; + /** + If struct description object si has a setter proxy, return it (a + function), else return undefined. + */ + const memberSetterProxy = function(si) { + return si.set || (si.adaptSet ? StructBinder.adaptSet(si.adaptSet) : void 0); + }; + /** + To be called by makeMemberWrapper() when si has a 'members' + member, i.e. is an embedded struct. This function sets up that + struct like any other and also sets up property accessor for + ctor.memberKey(name) which returns an instance of that new + StructType when the member is accessed. That instance wraps the + memory of the member's part of the containing C struct instance. + + That is, if struct Foo has member bar which is an inner struct + then: + + const f = new Foo; + const b = f.bar; + assert( b is-a StructType object ); + assert( b.pointer === f.b.pointer ); + + b will be disposed of when f() is. Calling b.dispose() will not + do any permanent harm, as the wrapper object will be recreated + when accessing f.bar, pointing to the same memory in f. + + The si.zeroOnDispose flag has no effect on embedded structs because + they wrap "external" memory, so do not own it, and are thus never + freed, as such. + */ + const makeMemberStructWrapper = function callee(ctor, name, si) { + /** + Where we store inner-struct member proxies. Keys are a + combination of the parent object's pointer address and the + property's name. The values are StructType instances. + */ + const __innerStructs = callee.innerStructs ??= /* @__PURE__ */ new Map(); + const key = ctor.memberKey(name); + if (void 0 !== si.signature) toss("'signature' cannot be used on an embedded struct (", ctor.structName, ".", key, ")."); + if (memberSetterProxy(si)) toss("'set' and 'adaptSet' are not permitted for nested struct members."); + si.structName ??= ctor.structName + "::" + name; + si.key = key; + si.name = name; + si.constructor = this.call(this, si.structName, si); + const getterProxy = memberGetterProxy(si); + const prop = Object.assign(Object.create(null), { + configurable: false, + enumerable: false, + set: __propThrowOnSet(ctor.structName, key), + get: function() { + const dbg = this.debugFlags.__flags; + const p = this.pointer; + const k = p + "." + key; + let s = __innerStructs.get(k); + if (dbg.getter) log("debug.getter: k =", k); + if (!s) { + s = new si.constructor(__ptrAdd(p, si.offset)); + __innerStructs.set(k, s); + this.addOnDispose(() => s.dispose()); + s.addOnDispose(() => __innerStructs.delete(k)); + } + if (getterProxy) s = getterProxy.apply(this, [s, key]); + if (dbg.getter) log("debug.getter: result =", s); + return s; + } + }); + Object.defineProperty(ctor.prototype, key, prop); + }; + /** + This is where most of the magic happens. + + Pass this a StructBinderImpl-generated constructor, a member + property name, and the struct member description object. It will + define property accessors for proto[memberKey] which read + from/write to memory in this.pointer. It modifies si to make + certain downstream operations simpler. + */ + const makeMemberWrapper = function f(ctor, name, si) { + si = __adaptStruct2(this, si); + if (si.members) return makeMemberStructWrapper.call(this, ctor, name, si); + if (!f.cache) { + f.cache = { + getters: {}, + setters: {}, + sw: {} + }; + const a = [ + "i", + "c", + "C", + "p", + "P", + "s", + "f", + "d", + "v()" + ]; + if (bigIntEnabled) a.push("j"); + a.forEach(function(v) { + f.cache.getters[v] = sigDVGetter(v); + f.cache.setters[v] = sigDVSetter(v); + f.cache.sw[v] = sigDVSetWrapper(v); + }); + f.sigCheck = function(obj, name, key, sig) { + if (Object.prototype.hasOwnProperty.call(obj, key)) toss(obj.structName, "already has a property named", key + "."); + looksLikeASig(sig) || toss("Malformed signature for", sPropName(obj.structName, name) + ":", sig); + }; + } + const key = ctor.memberKey(name); + f.sigCheck(ctor.prototype, name, key, si.signature); + si.key = key; + si.name = name; + const sigGlyph = sigLetter(si.signature); + const xPropName = sPropName(ctor.structName, key); + const dbg = ctor.debugFlags.__flags; + const getterProxy = memberGetterProxy(si); + const prop = Object.create(null); + prop.configurable = false; + prop.enumerable = false; + prop.get = function() { + /** + This getter proxy reads its value from the appropriate pointer + address in the heap. It knows where and how much to read based on + this.pointer, si.offset, and si.sizeof. + */ + if (dbg.getter) log("debug.getter:", f.cache.getters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof); + let rc = new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.getters[sigGlyph]](0, isLittleEndian); + if (getterProxy) rc = getterProxy.apply(this, [key, rc]); + if (dbg.getter) log("debug.getter:", xPropName, "result =", rc); + return rc; + }; + if (si.readOnly) prop.set = __propThrowOnSet(ctor.prototype.structName, key); + else { + const setterProxy = memberSetterProxy(si); + prop.set = function(v) { + /** + The converse of prop.get(), this encodes v into the appropriate + spot in the WASM heap. + */ + if (dbg.setter) log("debug.setter:", f.cache.setters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof, v); + if (!this.pointer) toss("Cannot set native property on a disposed", this.structName, "instance."); + if (setterProxy) v = setterProxy.apply(this, [key, v]); + if (null === v || void 0 === v) v = __NullPtr; + else if (isPtrSig(si.signature) && !__isPtr(v)) if (isAutoPtrSig(si.signature) && v instanceof StructType) { + v = v.pointer || __NullPtr; + if (dbg.setter) log("debug.setter:", xPropName, "resolved to", v); + } else toss("Invalid value for pointer-type", xPropName + "."); + new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian); + }; + } + Object.defineProperty(ctor.prototype, key, prop); + }; + /** + The main factory function which will be returned to the + caller. The third argument is structly for internal use. + + This level of indirection is to avoid that clients can pass a + third argument to this, as that's only for internal use. + + internalOpt options: + + - None right now. This is for potential use in recursion. + + Usages: + + StructBinder(string, obj [,optObj]); + StructBinder(obj); + */ + const StructBinderImpl = function StructBinderImpl(structName, si, opt = Object.create(null)) { + /** + StructCtor is the eventual return value of this function. We + need to populate this early on so that we can do some trickery + in feeding it through recursion. + + Uses: + + // heap-allocated: + const x = new StructCtor; + // externally-managed memory: + const y = new StructCtor( aPtrToACompatibleCStruct ); + + or, more recently: + + const z = new StructCtor({ + extraBytes: [int=0] extra bytes to allocate after the struct + + wrap: [aPtrToACompatibleCStruct=undefined]. If provided, this + instance waps, but does not (by default) own the memory, else + a new instance is allocated from the WASM heap. + + ownsPointer: true if this object takes over ownership of + wrap. + + zeroOnDispose: [bool=StructCtor.structInfo.zeroOnDispose] + + autoCalcSizeOffset: [bool=false] Automatically calculate + sizeof an offset. This is fine for pure-JS structs (which + probably aren't useful beyond testing of Jaccwabyt) but it's + dangerous to use with actual WASM objects because we cannot + be guaranteed to have the same memory layout as an ostensibly + matching C struct. This applies recursively to all children + of the struct description. + + // TODO? Per-instance overrides of the struct-level flags? + + get: (k,v)=>v, + set: (k,v)=>v, + adaptGet: string, + adaptSet: string + + // That wouldn't fit really well right now, apparently. + }); + + */ + const StructCtor = function StructCtor(arg) { + if (!(this instanceof StructCtor)) toss("The", structName, "constructor may only be called via 'new'."); + __allocStruct(StructCtor, this, ...arguments); + }; + const self = this; + /** + "Convert" struct description x to a struct description, if + needed. This expands adaptStruct() mappings and transforms + {memberName:signatureString} signature syntax to object form. + */ + const ads = (x) => { + return "string" === typeof x && looksLikeASig(x) ? { signature: x } : __adaptStruct2(self, x); + }; + if (1 === arguments.length) { + si = ads(structName); + structName = si.structName || si.name; + } else if (2 === arguments.length) { + si = ads(si); + si.name ??= structName; + } else si = ads(si); + structName ??= si.structName; + structName ??= opt.structName; + if (!structName) toss("One of 'name' or 'structName' are required."); + if (si.adapt) { + Object.keys(si.adapt.struct || {}).forEach((k) => { + __adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]); + }); + Object.keys(si.adapt.set || {}).forEach((k) => { + __adaptSet.call(StructBinderImpl, k, si.adapt.set[k]); + }); + Object.keys(si.adapt.get || {}).forEach((k) => { + __adaptGet.call(StructBinderImpl, k, si.adapt.get[k]); + }); + } + if (!si.members && !si.sizeof) si.sizeof = sigSize(si.signature); + const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags)); + Object.defineProperties(StructCtor, { + debugFlags, + isA: rop((v) => v instanceof StructCtor), + memberKey: __memberKeyProp, + memberKeys: __structMemberKeys, + structInfo: rop(si), + structName: rop(structName), + ptrAdd: rop(__ptrAdd) + }); + StructCtor.prototype = new StructType(structName, si, rop); + Object.defineProperties(StructCtor.prototype, { + debugFlags, + constructor: rop(StructCtor), + ptrAdd: rop(__ptrAddSelf) + }); + let lastMember = false; + let offset = 0; + const autoCalc = !!si.autoCalcSizeOffset; + if (!autoCalc) { + if (!si.sizeof) toss(structName, "description is missing its sizeof property."); + si.offset ??= 0; + } else si.offset ??= 0; + Object.keys(si.members || {}).forEach((k) => { + let m = ads(si.members[k]); + if (!m.members && !m.sizeof) { + m.sizeof = sigSize(m.signature); + if (!m.sizeof) toss(sPropName(structName, k), "is missing a sizeof property.", m); + } + if (void 0 === m.offset) if (autoCalc) m.offset = offset; + else toss(sPropName(structName, k), "is missing its offset.", JSON.stringify(m)); + si.members[k] = m; + if (!lastMember || lastMember.offset < m.offset) lastMember = m; + const oldAutoCalc = !!m.autoCalc; + if (autoCalc) m.autoCalcSizeOffset = true; + makeMemberWrapper.call(self, StructCtor, k, m); + if (oldAutoCalc) m.autoCalcSizeOffset = true; + else delete m.autoCalcSizeOffset; + offset += m.sizeof; + }); + if (!lastMember) toss("No member property descriptions found."); + if (!si.sizeof) si.sizeof = offset; + if (si.sizeof === 1) si.signature === "c" || si.signature === "C" || toss("Unexpected sizeof==1 member", sPropName(structName, k), "with signature", si.signature); + else { + if (0 !== si.sizeof % 4) { + console.warn("Invalid struct member description", si); + toss(structName, "sizeof is not aligned. sizeof=" + si.sizeof); + } + if (0 !== si.offset % 4) { + console.warn("Invalid struct member description", si); + toss(structName, "offset is not aligned. offset=" + si.offset); + } + } + if (si.sizeof < offset) { + console.warn("Suspect struct description:", si, "offset =", offset); + toss("Mismatch in the calculated vs. the provided sizeof/offset info.", "Expected sizeof", offset, "but got", si.sizeof, "for", si); + } + delete si.autoCalcSizeOffset; + return StructCtor; + }; + const StructBinder = function StructBinder(structName, structInfo) { + return 1 == arguments.length ? StructBinderImpl.call(StructBinder, structName) : StructBinderImpl.call(StructBinder, structName, structInfo); + }; + StructBinder.StructType = StructType; + StructBinder.config = config; + StructBinder.allocCString = __allocCString; + StructBinder.adaptGet = __adaptGet; + StructBinder.adaptSet = __adaptSet; + StructBinder.adaptStruct = __adaptStruct; + StructBinder.ptrAdd = __ptrAdd; + if (!StructBinder.debugFlags) StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags); + return StructBinder; + }; + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + "use strict"; + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; + globalThis.WhWasmUtilInstaller(wasm); + delete globalThis.WhWasmUtilInstaller; + /** + Signatures for the WASM-exported C-side functions. Each entry + is an array with 2+ elements: + + [ "c-side name", + "result type" (wasm.xWrap() syntax), + [arg types in xWrap() syntax] + // ^^^ this needn't strictly be an array: it can be subsequent + // elements instead: [x,y,z] is equivalent to x,y,z + ] + + Support for the API-specific data types in the result/argument + type strings gets plugged in at a later phase in the API + initialization process. + */ + const bindingSignatures = { + core: [ + [ + "sqlite3_aggregate_context", + "void*", + "sqlite3_context*", + "int" + ], + [ + "sqlite3_bind_double", + "int", + "sqlite3_stmt*", + "int", + "f64" + ], + [ + "sqlite3_bind_int", + "int", + "sqlite3_stmt*", + "int", + "int" + ], + [ + "sqlite3_bind_null", + void 0, + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_bind_parameter_count", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_bind_parameter_index", + "int", + "sqlite3_stmt*", + "string" + ], + [ + "sqlite3_bind_parameter_name", + "string", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_bind_pointer", + "int", + "sqlite3_stmt*", + "int", + "*", + "string:static", + "*" + ], + [ + "sqlite3_busy_handler", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + signature: "i(pi)", + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_busy_timeout", + "int", + "sqlite3*", + "int" + ], + [ + "sqlite3_changes", + "int", + "sqlite3*" + ], + [ + "sqlite3_clear_bindings", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_collation_needed", + "int", + "sqlite3*", + "*", + "*" + ], + [ + "sqlite3_column_blob", + "*", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_bytes", + "int", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_count", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_column_decltype", + "string", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_double", + "f64", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_int", + "int", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_name", + "string", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_type", + "int", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_value", + "sqlite3_value*", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_commit_hook", + "void*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_commit_hook", + signature: "i(p)", + contextKey: (argv) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_compileoption_get", + "string", + "int" + ], + [ + "sqlite3_compileoption_used", + "int", + "string" + ], + [ + "sqlite3_complete", + "int", + "string:flexible" + ], + [ + "sqlite3_context_db_handle", + "sqlite3*", + "sqlite3_context*" + ], + [ + "sqlite3_data_count", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_db_filename", + "string", + "sqlite3*", + "string" + ], + [ + "sqlite3_db_handle", + "sqlite3*", + "sqlite3_stmt*" + ], + [ + "sqlite3_db_name", + "string", + "sqlite3*", + "int" + ], + [ + "sqlite3_db_readonly", + "int", + "sqlite3*", + "string" + ], + [ + "sqlite3_db_status", + "int", + "sqlite3*", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_errcode", + "int", + "sqlite3*" + ], + [ + "sqlite3_errmsg", + "string", + "sqlite3*" + ], + [ + "sqlite3_error_offset", + "int", + "sqlite3*" + ], + [ + "sqlite3_errstr", + "string", + "int" + ], + [ + "sqlite3_exec", + "int", + [ + "sqlite3*", + "string:flexible", + new wasm.xWrap.FuncPtrAdapter({ + signature: "i(pipp)", + bindScope: "transient", + callProxy: (callback) => { + let aNames; + return (pVoid, nCols, pColVals, pColNames) => { + try { + const aVals = wasm.cArgvToJs(nCols, pColVals); + if (!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); + return callback(aVals, aNames) | 0; + } catch (e) { + return e.resultCode || capi.SQLITE_ERROR; + } + }; + } + }), + "*", + "**" + ] + ], + [ + "sqlite3_expanded_sql", + "string", + "sqlite3_stmt*" + ], + [ + "sqlite3_extended_errcode", + "int", + "sqlite3*" + ], + [ + "sqlite3_extended_result_codes", + "int", + "sqlite3*", + "int" + ], + [ + "sqlite3_file_control", + "int", + "sqlite3*", + "string", + "int", + "*" + ], + [ + "sqlite3_finalize", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_free", + void 0, + "*" + ], + [ + "sqlite3_get_autocommit", + "int", + "sqlite3*" + ], + [ + "sqlite3_get_auxdata", + "*", + "sqlite3_context*", + "int" + ], + ["sqlite3_initialize", void 0], + [ + "sqlite3_interrupt", + void 0, + "sqlite3*" + ], + [ + "sqlite3_is_interrupted", + "int", + "sqlite3*" + ], + ["sqlite3_keyword_count", "int"], + [ + "sqlite3_keyword_name", + "int", + [ + "int", + "**", + "*" + ] + ], + [ + "sqlite3_keyword_check", + "int", + ["string", "int"] + ], + ["sqlite3_libversion", "string"], + ["sqlite3_libversion_number", "int"], + [ + "sqlite3_limit", + "int", + [ + "sqlite3*", + "int", + "int" + ] + ], + [ + "sqlite3_malloc", + "*", + "int" + ], + [ + "sqlite3_next_stmt", + "sqlite3_stmt*", + ["sqlite3*", "sqlite3_stmt*"] + ], + [ + "sqlite3_open", + "int", + "string", + "*" + ], + [ + "sqlite3_open_v2", + "int", + "string", + "*", + "int", + "string" + ], + [ + "sqlite3_realloc", + "*", + "*", + "int" + ], + [ + "sqlite3_reset", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_result_blob", + void 0, + "sqlite3_context*", + "*", + "int", + "*" + ], + [ + "sqlite3_result_double", + void 0, + "sqlite3_context*", + "f64" + ], + [ + "sqlite3_result_error", + void 0, + "sqlite3_context*", + "string", + "int" + ], + [ + "sqlite3_result_error_code", + void 0, + "sqlite3_context*", + "int" + ], + [ + "sqlite3_result_error_nomem", + void 0, + "sqlite3_context*" + ], + [ + "sqlite3_result_error_toobig", + void 0, + "sqlite3_context*" + ], + [ + "sqlite3_result_int", + void 0, + "sqlite3_context*", + "int" + ], + [ + "sqlite3_result_null", + void 0, + "sqlite3_context*" + ], + [ + "sqlite3_result_pointer", + void 0, + "sqlite3_context*", + "*", + "string:static", + "*" + ], + [ + "sqlite3_result_subtype", + void 0, + "sqlite3_value*", + "int" + ], + [ + "sqlite3_result_text", + void 0, + "sqlite3_context*", + "string", + "int", + "*" + ], + [ + "sqlite3_result_zeroblob", + void 0, + "sqlite3_context*", + "int" + ], + [ + "sqlite3_rollback_hook", + "void*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_rollback_hook", + signature: "v(p)", + contextKey: (argv) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_set_auxdata", + void 0, + [ + "sqlite3_context*", + "int", + "*", + "*" + ] + ], + [ + "sqlite3_set_errmsg", + "int", + "sqlite3*", + "int", + "string" + ], + ["sqlite3_shutdown", void 0], + ["sqlite3_sourceid", "string"], + [ + "sqlite3_sql", + "string", + "sqlite3_stmt*" + ], + [ + "sqlite3_status", + "int", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_step", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_stmt_busy", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_stmt_readonly", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_stmt_status", + "int", + "sqlite3_stmt*", + "int", + "int" + ], + [ + "sqlite3_strglob", + "int", + "string", + "string" + ], + [ + "sqlite3_stricmp", + "int", + "string", + "string" + ], + [ + "sqlite3_strlike", + "int", + "string", + "string", + "int" + ], + [ + "sqlite3_strnicmp", + "int", + "string", + "string", + "int" + ], + [ + "sqlite3_table_column_metadata", + "int", + "sqlite3*", + "string", + "string", + "string", + "**", + "**", + "*", + "*", + "*" + ], + [ + "sqlite3_total_changes", + "int", + "sqlite3*" + ], + [ + "sqlite3_trace_v2", + "int", + [ + "sqlite3*", + "int", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_trace_v2::callback", + signature: "i(ippp)", + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_txn_state", + "int", + ["sqlite3*", "string"] + ], + [ + "sqlite3_uri_boolean", + "int", + "sqlite3_filename", + "string", + "int" + ], + [ + "sqlite3_uri_key", + "string", + "sqlite3_filename", + "int" + ], + [ + "sqlite3_uri_parameter", + "string", + "sqlite3_filename", + "string" + ], + [ + "sqlite3_user_data", + "void*", + "sqlite3_context*" + ], + [ + "sqlite3_value_blob", + "*", + "sqlite3_value*" + ], + [ + "sqlite3_value_bytes", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_double", + "f64", + "sqlite3_value*" + ], + [ + "sqlite3_value_dup", + "sqlite3_value*", + "sqlite3_value*" + ], + [ + "sqlite3_value_free", + void 0, + "sqlite3_value*" + ], + [ + "sqlite3_value_frombind", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_int", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_nochange", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_numeric_type", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_pointer", + "*", + "sqlite3_value*", + "string:static" + ], + [ + "sqlite3_value_subtype", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_type", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_vfs_find", + "*", + "string" + ], + [ + "sqlite3_vfs_register", + "int", + "sqlite3_vfs*", + "int" + ], + [ + "sqlite3_vfs_unregister", + "int", + "sqlite3_vfs*" + ] + ], + int64: [ + [ + "sqlite3_bind_int64", + "int", + [ + "sqlite3_stmt*", + "int", + "i64" + ] + ], + [ + "sqlite3_changes64", + "i64", + ["sqlite3*"] + ], + [ + "sqlite3_column_int64", + "i64", + ["sqlite3_stmt*", "int"] + ], + [ + "sqlite3_deserialize", + "int", + "sqlite3*", + "string", + "*", + "i64", + "i64", + "int" + ], + [ + "sqlite3_last_insert_rowid", + "i64", + ["sqlite3*"] + ], + [ + "sqlite3_malloc64", + "*", + "i64" + ], + [ + "sqlite3_msize", + "i64", + "*" + ], + [ + "sqlite3_overload_function", + "int", + [ + "sqlite3*", + "string", + "int" + ] + ], + [ + "sqlite3_realloc64", + "*", + "*", + "i64" + ], + [ + "sqlite3_result_int64", + void 0, + "*", + "i64" + ], + [ + "sqlite3_result_zeroblob64", + "int", + "*", + "i64" + ], + [ + "sqlite3_serialize", + "*", + "sqlite3*", + "string", + "*", + "int" + ], + [ + "sqlite3_set_last_insert_rowid", + void 0, + ["sqlite3*", "i64"] + ], + [ + "sqlite3_status64", + "int", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_db_status64", + "int", + "sqlite3*", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_total_changes64", + "i64", + ["sqlite3*"] + ], + [ + "sqlite3_update_hook", + "*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_update_hook::callback", + signature: "v(pippj)", + contextKey: (argv) => argv[0], + callProxy: (callback) => { + return (p, op, z0, z1, rowid) => { + callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid); + }; + } + }), + "*" + ] + ], + [ + "sqlite3_uri_int64", + "i64", + [ + "sqlite3_filename", + "string", + "i64" + ] + ], + [ + "sqlite3_value_int64", + "i64", + "sqlite3_value*" + ] + ], + wasmInternal: [ + [ + "sqlite3__wasm_db_reset", + "int", + "sqlite3*" + ], + [ + "sqlite3__wasm_db_vfs", + "sqlite3_vfs*", + "sqlite3*", + "string" + ], + [ + "sqlite3__wasm_vfs_create_file", + "int", + "sqlite3_vfs*", + "string", + "*", + "int" + ], + [ + "sqlite3__wasm_posix_create_file", + "int", + "string", + "*", + "int" + ], + [ + "sqlite3__wasm_vfs_unlink", + "int", + "sqlite3_vfs*", + "string" + ], + [ + "sqlite3__wasm_qfmt_token", + "string:dealloc", + "string", + "int" + ] + ] + }; + if (!!wasm.exports.sqlite3_progress_handler) bindingSignatures.core.push([ + "sqlite3_progress_handler", + void 0, + [ + "sqlite3*", + "int", + new wasm.xWrap.FuncPtrAdapter({ + name: "xProgressHandler", + signature: "i(p)", + bindScope: "context", + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ]); + if (!!wasm.exports.sqlite3_stmt_explain) bindingSignatures.core.push([ + "sqlite3_stmt_explain", + "int", + "sqlite3_stmt*", + "int" + ], [ + "sqlite3_stmt_isexplain", + "int", + "sqlite3_stmt*" + ]); + if (!!wasm.exports.sqlite3_set_authorizer) bindingSignatures.core.push([ + "sqlite3_set_authorizer", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_set_authorizer::xAuth", + signature: "i(pissss)", + contextKey: (argv, argIndex) => argv[0], + callProxy: (callback) => { + return (pV, iCode, s0, s1, s2, s3) => { + try { + s0 = s0 && wasm.cstrToJs(s0); + s1 = s1 && wasm.cstrToJs(s1); + s2 = s2 && wasm.cstrToJs(s2); + s3 = s3 && wasm.cstrToJs(s3); + return callback(pV, iCode, s0, s1, s2, s3) | 0; + } catch (e) { + return e.resultCode || capi.SQLITE_ERROR; + } + }; + } + }), + "*" + ] + ]); + if (!!wasm.exports.sqlite3_column_origin_name) bindingSignatures.core.push([ + "sqlite3_column_database_name", + "string", + "sqlite3_stmt*", + "int" + ], [ + "sqlite3_column_origin_name", + "string", + "sqlite3_stmt*", + "int" + ], [ + "sqlite3_column_table_name", + "string", + "sqlite3_stmt*", + "int" + ]); + if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_declare_vtab) bindingSignatures.int64.push([ + "sqlite3_create_module", + "int", + [ + "sqlite3*", + "string", + "sqlite3_module*", + "*" + ] + ], [ + "sqlite3_create_module_v2", + "int", + [ + "sqlite3*", + "string", + "sqlite3_module*", + "*", + "*" + ] + ], [ + "sqlite3_declare_vtab", + "int", + ["sqlite3*", "string:flexible"] + ], [ + "sqlite3_drop_modules", + "int", + ["sqlite3*", "**"] + ], [ + "sqlite3_vtab_collation", + "string", + "sqlite3_index_info*", + "int" + ], [ + "sqlite3_vtab_distinct", + "int", + "sqlite3_index_info*" + ], [ + "sqlite3_vtab_in", + "int", + "sqlite3_index_info*", + "int", + "int" + ], [ + "sqlite3_vtab_in_first", + "int", + "sqlite3_value*", + "**" + ], [ + "sqlite3_vtab_in_next", + "int", + "sqlite3_value*", + "**" + ], [ + "sqlite3_vtab_nochange", + "int", + "sqlite3_context*" + ], [ + "sqlite3_vtab_on_conflict", + "int", + "sqlite3*" + ], [ + "sqlite3_vtab_rhs_value", + "int", + "sqlite3_index_info*", + "int", + "**" + ]); + if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_preupdate_hook) bindingSignatures.int64.push([ + "sqlite3_preupdate_blobwrite", + "int", + "sqlite3*" + ], [ + "sqlite3_preupdate_count", + "int", + "sqlite3*" + ], [ + "sqlite3_preupdate_depth", + "int", + "sqlite3*" + ], [ + "sqlite3_preupdate_hook", + "*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_preupdate_hook", + signature: "v(ppippjj)", + contextKey: (argv) => argv[0], + callProxy: (callback) => { + return (p, db, op, zDb, zTbl, iKey1, iKey2) => { + callback(p, db, op, wasm.cstrToJs(zDb), wasm.cstrToJs(zTbl), iKey1, iKey2); + }; + } + }), + "*" + ] + ], [ + "sqlite3_preupdate_new", + "int", + [ + "sqlite3*", + "int", + "**" + ] + ], [ + "sqlite3_preupdate_old", + "int", + [ + "sqlite3*", + "int", + "**" + ] + ]); + if (wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add && !!wasm.exports.sqlite3session_create && !!wasm.exports.sqlite3_preupdate_hook) { + /** + FuncPtrAdapter options for session-related callbacks with the + native signature "i(ps)". This proxy converts the 2nd argument + from a C string to a JS string before passing the arguments on + to the client-provided JS callback. + */ + const __ipsProxy = { + signature: "i(ps)", + callProxy: (callback) => { + return (p, s) => { + try { + return callback(p, wasm.cstrToJs(s)) | 0; + } catch (e) { + return e.resultCode || capi.SQLITE_ERROR; + } + }; + } + }; + bindingSignatures.int64.push([ + "sqlite3changegroup_add", + "int", + [ + "sqlite3_changegroup*", + "int", + "void*" + ] + ], [ + "sqlite3changegroup_add_strm", + "int", + [ + "sqlite3_changegroup*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changegroup_delete", + void 0, + ["sqlite3_changegroup*"] + ], [ + "sqlite3changegroup_new", + "int", + ["**"] + ], [ + "sqlite3changegroup_output", + "int", + [ + "sqlite3_changegroup*", + "int*", + "**" + ] + ], [ + "sqlite3changegroup_output_strm", + "int", + [ + "sqlite3_changegroup*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppi)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_apply", + "int", + [ + "sqlite3*", + "int", + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_apply_strm", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_apply_v2", + "int", + [ + "sqlite3*", + "int", + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_apply_v2_strm", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_apply_v3", + "int", + [ + "sqlite3*", + "int", + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + signature: "i(pp)", + bindScope: "transient" + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_apply_v3_strm", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + signature: "i(pp)", + bindScope: "transient" + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_concat", + "int", + [ + "int", + "void*", + "int", + "void*", + "int*", + "**" + ] + ], [ + "sqlite3changeset_concat_strm", + "int", + [ + new wasm.xWrap.FuncPtrAdapter({ + name: "xInputA", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInputB", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppi)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_conflict", + "int", + [ + "sqlite3_changeset_iter*", + "int", + "**" + ] + ], [ + "sqlite3changeset_finalize", + "int", + ["sqlite3_changeset_iter*"] + ], [ + "sqlite3changeset_fk_conflicts", + "int", + ["sqlite3_changeset_iter*", "int*"] + ], [ + "sqlite3changeset_invert", + "int", + [ + "int", + "void*", + "int*", + "**" + ] + ], [ + "sqlite3changeset_invert_strm", + "int", + [ + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppi)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_new", + "int", + [ + "sqlite3_changeset_iter*", + "int", + "**" + ] + ], [ + "sqlite3changeset_next", + "int", + ["sqlite3_changeset_iter*"] + ], [ + "sqlite3changeset_old", + "int", + [ + "sqlite3_changeset_iter*", + "int", + "**" + ] + ], [ + "sqlite3changeset_op", + "int", + [ + "sqlite3_changeset_iter*", + "**", + "int*", + "int*", + "int*" + ] + ], [ + "sqlite3changeset_pk", + "int", + [ + "sqlite3_changeset_iter*", + "**", + "int*" + ] + ], [ + "sqlite3changeset_start", + "int", + [ + "**", + "int", + "*" + ] + ], [ + "sqlite3changeset_start_strm", + "int", + [ + "**", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_start_v2", + "int", + [ + "**", + "int", + "*", + "int" + ] + ], [ + "sqlite3changeset_start_v2_strm", + "int", + [ + "**", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + "int" + ] + ], [ + "sqlite3session_attach", + "int", + ["sqlite3_session*", "string"] + ], [ + "sqlite3session_changeset", + "int", + [ + "sqlite3_session*", + "int*", + "**" + ] + ], [ + "sqlite3session_changeset_size", + "i64", + ["sqlite3_session*"] + ], [ + "sqlite3session_changeset_strm", + "int", + [ + "sqlite3_session*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3session_config", + "int", + ["int", "void*"] + ], [ + "sqlite3session_create", + "int", + [ + "sqlite3*", + "string", + "**" + ] + ], [ + "sqlite3session_diff", + "int", + [ + "sqlite3_session*", + "string", + "string", + "**" + ] + ], [ + "sqlite3session_enable", + "int", + ["sqlite3_session*", "int"] + ], [ + "sqlite3session_indirect", + "int", + ["sqlite3_session*", "int"] + ], [ + "sqlite3session_isempty", + "int", + ["sqlite3_session*"] + ], [ + "sqlite3session_memory_used", + "i64", + ["sqlite3_session*"] + ], [ + "sqlite3session_object_config", + "int", + [ + "sqlite3_session*", + "int", + "void*" + ] + ], [ + "sqlite3session_patchset", + "int", + [ + "sqlite3_session*", + "*", + "**" + ] + ], [ + "sqlite3session_patchset_strm", + "int", + [ + "sqlite3_session*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3session_table_filter", + void 0, + [ + "sqlite3_session*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + ...__ipsProxy, + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ]); + } + /** + Prepare JS<->C struct bindings for the non-opaque struct types we + need... + */ + sqlite3.StructBinder = globalThis.Jaccwabyt({ + heap: wasm.heap8u, + alloc: wasm.alloc, + dealloc: wasm.dealloc, + bigIntEnabled: wasm.bigIntEnabled, + pointerIR: wasm.ptr.ir, + memberPrefix: "$" + }); + delete globalThis.Jaccwabyt; + { + const __xString = wasm.xWrap.argAdapter("string"); + wasm.xWrap.argAdapter("string:flexible", (v) => __xString(util.flexibleString(v))); + /** + The 'string:static' argument adapter treats its argument as + either... + + - WASM pointer: assumed to be a long-lived C-string which gets + returned as-is. + + - Anything else: gets coerced to a JS string for use as a map + key. If a matching entry is found (as described next), it is + returned, else wasm.allocCString() is used to create a a new + string, map its pointer to a copy of (''+v) for the remainder + of the application's life, and returns that pointer value for + this call and all future calls which are passed a + string-equivalent argument. + + Use case: sqlite3_bind_pointer(), sqlite3_result_pointer(), and + sqlite3_value_pointer() call for "a static string and + preferably a string literal". This converter is used to ensure + that the string value seen by those functions is long-lived and + behaves as they need it to, at the cost of a one-time leak of + each distinct key. + */ + wasm.xWrap.argAdapter("string:static", function(v) { + if (wasm.isPtr(v)) return v; + v = "" + v; + return this[v] || (this[v] = wasm.allocCString(v)); + }.bind(Object.create(null))); + /** + Add some descriptive xWrap() aliases for '*' intended to (A) + improve readability/correctness of bindingSignatures and (B) + provide automatic conversion from higher-level representations, + e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via (capi.sqlite3_vfs + instance).pointer. + */ + const __xArgPtr = wasm.xWrap.argAdapter("*"); + const nilType = function() {}; + wasm.xWrap.argAdapter("sqlite3_filename", __xArgPtr)("sqlite3_context*", __xArgPtr)("sqlite3_value*", __xArgPtr)("void*", __xArgPtr)("sqlite3_changegroup*", __xArgPtr)("sqlite3_changeset_iter*", __xArgPtr)("sqlite3_session*", __xArgPtr)("sqlite3_stmt*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.Stmt || nilType) ? v.pointer : v))("sqlite3*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.DB || nilType) ? v.pointer : v))("sqlite3_vfs*", (v) => { + if ("string" === typeof v) return capi.sqlite3_vfs_find(v) || sqlite3.SQLite3Error.toss(capi.SQLITE_NOTFOUND, "Unknown sqlite3_vfs name:", v); + return __xArgPtr(v instanceof (capi.sqlite3_vfs || nilType) ? v.pointer : v); + }); + if (wasm.exports.sqlite3_declare_vtab) wasm.xWrap.argAdapter("sqlite3_index_info*", (v) => __xArgPtr(v instanceof (capi.sqlite3_index_info || nilType) ? v.pointer : v))("sqlite3_module*", (v) => __xArgPtr(v instanceof (capi.sqlite3_module || nilType) ? v.pointer : v)); + /** + Alias `T*` to `*` for return type conversions for common T + types, primarily to improve legibility of their binding + signatures. + */ + const __xRcPtr = wasm.xWrap.resultAdapter("*"); + wasm.xWrap.resultAdapter("sqlite3*", __xRcPtr)("sqlite3_context*", __xRcPtr)("sqlite3_stmt*", __xRcPtr)("sqlite3_value*", __xRcPtr)("sqlite3_vfs*", __xRcPtr)("void*", __xRcPtr); + /** + Populate api object with sqlite3_...() by binding the "raw" wasm + exports into type-converting proxies using wasm.xWrap(). + */ + for (const e of bindingSignatures.core) capi[e[0]] = wasm.xWrap.apply(null, e); + for (const e of bindingSignatures.wasmInternal) util[e[0]] = wasm.xWrap.apply(null, e); + for (const e of bindingSignatures.int64) capi[e[0]] = wasm.bigIntEnabled ? wasm.xWrap.apply(null, e) : () => toss(e[0] + "() is unavailable due to lack", "of BigInt support in this build."); + delete bindingSignatures.core; + delete bindingSignatures.int64; + delete bindingSignatures.wasmInternal; + /** + Sets the given db's error state. Accepts: + + - (sqlite3*, int code, string msg) + - (sqlite3*, Error e [,string msg = ''+e]) + + If passed a WasmAllocError, the message is ignored and the + result code is SQLITE_NOMEM. If passed any other Error type, + the result code defaults to SQLITE_ERROR unless the Error + object has a resultCode property, in which case that is used + (e.g. SQLite3Error has that). If passed a non-WasmAllocError + exception, the message string defaults to ''+theError. + + Returns either the final result code, capi.SQLITE_NOMEM if + setting the message string triggers an OOM, or + capi.SQLITE_MISUSE if pDb is NULL or invalid (with the caveat + that behavior in the later case is undefined if pDb is not + "valid enough"). + + Pass (pDb,0,0) to clear the error state. + */ + util.sqlite3__wasm_db_error = function(pDb, resultCode, message) { + if (!pDb) return capi.SQLITE_MISUSE; + if (resultCode instanceof sqlite3.WasmAllocError) { + resultCode = capi.SQLITE_NOMEM; + message = 0; + } else if (resultCode instanceof Error) { + message = message || "" + resultCode; + resultCode = resultCode.resultCode || capi.SQLITE_ERROR; + } + return capi.sqlite3_set_errmsg(pDb, resultCode, message) || resultCode; + }; + } + { + const cJson = wasm.xCall("sqlite3__wasm_enum_json"); + if (!cJson) toss("Maintenance required: increase sqlite3__wasm_enum_json()'s", "static buffer size!"); + wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); + const defineGroups = [ + "access", + "authorizer", + "blobFinalizers", + "changeset", + "config", + "dataTypes", + "dbConfig", + "dbStatus", + "encodings", + "fcntl", + "flock", + "ioCap", + "limits", + "openFlags", + "prepareFlags", + "resultCodes", + "sqlite3Status", + "stmtStatus", + "syncFlags", + "trace", + "txnState", + "udfFlags", + "version" + ]; + if (wasm.bigIntEnabled) defineGroups.push("serialize", "session", "vtab"); + for (const t of defineGroups) for (const e of Object.entries(wasm.ctype[t])) capi[e[0]] = e[1]; + if (!wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)) toss("Internal error: cannot resolve exported function", "entry SQLITE_WASM_DEALLOC (==" + capi.SQLITE_WASM_DEALLOC + ")."); + const __rcMap = Object.create(null); + for (const e of Object.entries(wasm.ctype["resultCodes"])) __rcMap[e[1]] = e[0]; + /** + For the given integer, returns the SQLITE_xxx result code as a + string, or undefined if no such mapping is found. + */ + capi.sqlite3_js_rc_str = (rc) => __rcMap[rc]; + const notThese = Object.assign(Object.create(null), { + WasmTestStruct: true, + sqlite3_index_info: !wasm.bigIntEnabled, + sqlite3_index_constraint: !wasm.bigIntEnabled, + sqlite3_index_orderby: !wasm.bigIntEnabled, + sqlite3_index_constraint_usage: !wasm.bigIntEnabled + }); + for (const s of wasm.ctype.structs) if (!notThese[s.name]) capi[s.name] = sqlite3.StructBinder(s); + if (capi.sqlite3_index_info) { + for (const k of [ + "sqlite3_index_constraint", + "sqlite3_index_orderby", + "sqlite3_index_constraint_usage" + ]) { + capi.sqlite3_index_info[k] = capi[k]; + delete capi[k]; + } + capi.sqlite3_vtab_config = wasm.xWrap("sqlite3__wasm_vtab_config", "int", [ + "sqlite3*", + "int", + "int" + ]); + } + } + /** + Internal helper to assist in validating call argument counts in + the hand-written sqlite3_xyz() wrappers. We do this only for + consistency with non-special-case wrappings. + */ + const __dbArgcMismatch = (pDb, f, n) => { + return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, f + "() requires " + n + " argument" + (1 === n ? "" : "s") + "."); + }; + /** Code duplication reducer for functions which take an encoding + argument and require SQLITE_UTF8. Sets the db error code to + SQLITE_FORMAT, installs a descriptive error message, + and returns SQLITE_FORMAT. */ + const __errEncoding = (pDb) => { + return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding."); + }; + /** + __dbCleanupMap is infrastructure for recording registration of + UDFs and collations so that sqlite3_close_v2() can clean up any + automated JS-to-WASM function conversions installed by those. + */ + const __argPDb = (pDb) => wasm.xWrap.argAdapter("sqlite3*")(pDb); + const __argStr = (str) => wasm.isPtr(str) ? wasm.cstrToJs(str) : str; + const __dbCleanupMap = function(pDb, mode) { + pDb = __argPDb(pDb); + let m = this.dbMap.get(pDb); + if (!mode) { + this.dbMap.delete(pDb); + return m; + } else if (!m && mode > 0) this.dbMap.set(pDb, m = Object.create(null)); + return m; + }.bind(Object.assign(Object.create(null), { dbMap: /* @__PURE__ */ new Map() })); + __dbCleanupMap.addCollation = function(pDb, name) { + const m = __dbCleanupMap(pDb, 1); + if (!m.collation) m.collation = /* @__PURE__ */ new Set(); + m.collation.add(__argStr(name).toLowerCase()); + }; + __dbCleanupMap._addUDF = function(pDb, name, arity, map) { + name = __argStr(name).toLowerCase(); + let u = map.get(name); + if (!u) map.set(name, u = /* @__PURE__ */ new Set()); + u.add(arity < 0 ? -1 : arity); + }; + __dbCleanupMap.addFunction = function(pDb, name, arity) { + const m = __dbCleanupMap(pDb, 1); + if (!m.udf) m.udf = /* @__PURE__ */ new Map(); + this._addUDF(pDb, name, arity, m.udf); + }; + if (wasm.exports.sqlite3_create_window_function) __dbCleanupMap.addWindowFunc = function(pDb, name, arity) { + const m = __dbCleanupMap(pDb, 1); + if (!m.wudf) m.wudf = /* @__PURE__ */ new Map(); + this._addUDF(pDb, name, arity, m.wudf); + }; + /** + Intended to be called _only_ from sqlite3_close_v2(), + passed its non-0 db argument. + + This function frees up certain automatically-installed WASM + function bindings which were installed on behalf of the given db, + as those may otherwise leak. + + Notable caveat: this is only ever run via + sqlite3.capi.sqlite3_close_v2(). If a client, for whatever + reason, uses sqlite3.wasm.exports.sqlite3_close_v2() (the + function directly exported from WASM), this cleanup will not + happen. + + This is not a silver bullet for avoiding automation-related + leaks but represents "an honest effort." + + The issue being addressed here is covered at: + + https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr + */ + __dbCleanupMap.cleanup = function(pDb) { + pDb = __argPDb(pDb); + /** + Installing NULL functions in the C API will remove those + bindings. The FuncPtrAdapter which sits between us and the C + API will also treat that as an opportunity to + wasm.uninstallFunction() any WASM function bindings it has + installed for pDb. + */ + for (const obj of [ + ["sqlite3_busy_handler", 3], + ["sqlite3_commit_hook", 3], + ["sqlite3_preupdate_hook", 3], + ["sqlite3_progress_handler", 4], + ["sqlite3_rollback_hook", 3], + ["sqlite3_set_authorizer", 3], + ["sqlite3_trace_v2", 4], + ["sqlite3_update_hook", 3] + ]) { + const [name, arity] = obj; + if (!wasm.exports[name]) continue; + const closeArgs = [pDb]; + closeArgs.length = arity; + try { + capi[name](...closeArgs); + } catch (e) { + sqlite3.config.warn("close-time call of", name + "(", closeArgs, ") threw:", e); + } + } + const m = __dbCleanupMap(pDb, 0); + if (!m) return; + if (m.collation) { + for (const name of m.collation) try { + capi.sqlite3_create_collation_v2(pDb, name, capi.SQLITE_UTF8, 0, 0, 0); + } catch (e) {} + delete m.collation; + } + let i; + for (i = 0; i < 2; ++i) { + const fmap = i ? m.wudf : m.udf; + if (!fmap) continue; + const func = i ? capi.sqlite3_create_window_function : capi.sqlite3_create_function_v2; + for (const e of fmap) { + const name = e[0], arities = e[1]; + const fargs = [ + pDb, + name, + 0, + capi.SQLITE_UTF8, + 0, + 0, + 0, + 0, + 0 + ]; + if (i) fargs.push(0); + for (const arity of arities) try { + fargs[2] = arity; + func.apply(null, fargs); + } catch (e) {} + arities.clear(); + } + fmap.clear(); + } + delete m.udf; + delete m.wudf; + }; + { + const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*"); + capi.sqlite3_close_v2 = function(pDb) { + if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_close_v2", 1); + if (pDb) try { + __dbCleanupMap.cleanup(pDb); + } catch (e) {} + return __sqlite3CloseV2(pDb); + }; + } + if (capi.sqlite3session_create) { + const __sqlite3SessionDelete = wasm.xWrap("sqlite3session_delete", void 0, ["sqlite3_session*"]); + capi.sqlite3session_delete = function(pSession) { + if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3session_delete", 1); + else if (pSession) capi.sqlite3session_table_filter(pSession, 0, 0); + __sqlite3SessionDelete(pSession); + }; + } + { + const contextKey = (argv, argIndex) => { + return "argv[" + argIndex + "]:" + argv[0] + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); + }; + const __sqlite3CreateCollationV2 = wasm.xWrap("sqlite3_create_collation_v2", "int", [ + "sqlite3*", + "string", + "int", + "*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xCompare", + signature: "i(pipip)", + contextKey + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xDestroy", + signature: "v(p)", + contextKey + }) + ]); + /** + Works exactly like C's sqlite3_create_collation_v2() except that: + + 1) It returns capi.SQLITE_FORMAT if the 3rd argument contains + any encoding-related value other than capi.SQLITE_UTF8. No + other encodings are supported. As a special case, if the + bottom 4 bits of that argument are 0, SQLITE_UTF8 is + assumed. + + 2) It accepts JS functions for its function-pointer arguments, + for which it will install WASM-bound proxies. The bindings + are "permanent," in that they will stay in the WASM + environment until it shuts down unless the client calls this + again with the same collation name and a value of 0 or null + for the the function pointer(s). sqlite3_close_v2() will + also clean up such automatically-installed WASM functions. + + For consistency with the C API, it requires the same number of + arguments. It returns capi.SQLITE_MISUSE if passed any other + argument count. + + Returns 0 on success, non-0 on error, in which case the error + state of pDb (of type `sqlite3*` or argument-convertible to it) + may contain more information. + */ + capi.sqlite3_create_collation_v2 = function(pDb, zName, eTextRep, pArg, xCompare, xDestroy) { + if (6 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_collation_v2", 6); + else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; + else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); + try { + const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); + if (0 === rc && xCompare instanceof Function) __dbCleanupMap.addCollation(pDb, zName); + return rc; + } catch (e) { + return util.sqlite3__wasm_db_error(pDb, e); + } + }; + capi.sqlite3_create_collation = (pDb, zName, eTextRep, pArg, xCompare) => { + return 5 === arguments.length ? capi.sqlite3_create_collation_v2(pDb, zName, eTextRep, pArg, xCompare, 0) : __dbArgcMismatch(pDb, "sqlite3_create_collation", 5); + }; + } + { + /** FuncPtrAdapter for contextKey() for sqlite3_create_function() + and friends. */ + const contextKey = function(argv, argIndex) { + return argv[0] + ":" + (argv[2] < 0 ? -1 : argv[2]) + ":" + argIndex + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); + }; + /** + JS proxies for the various sqlite3_create[_window]_function() + callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter. + */ + const __cfProxy = Object.assign(Object.create(null), { + xInverseAndStep: { + signature: "v(pip)", + contextKey, + callProxy: (callback) => { + return (pCtx, argc, pArgv) => { + try { + callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)); + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xFinalAndValue: { + signature: "v(p)", + contextKey, + callProxy: (callback) => { + return (pCtx) => { + try { + capi.sqlite3_result_js(pCtx, callback(pCtx)); + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xFunc: { + signature: "v(pip)", + contextKey, + callProxy: (callback) => { + return (pCtx, argc, pArgv) => { + try { + capi.sqlite3_result_js(pCtx, callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))); + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xDestroy: { + signature: "v(p)", + contextKey, + callProxy: (callback) => { + return (pVoid) => { + try { + callback(pVoid); + } catch (e) { + console.error("UDF xDestroy method threw:", e); + } + }; + } + } + }); + const __sqlite3CreateFunction = wasm.xWrap("sqlite3_create_function_v2", "int", [ + "sqlite3*", + "string", + "int", + "int", + "*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFunc", + ...__cfProxy.xFunc + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xStep", + ...__cfProxy.xInverseAndStep + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xFinal", + ...__cfProxy.xFinalAndValue + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xDestroy", + ...__cfProxy.xDestroy + }) + ]); + const __sqlite3CreateWindowFunction = wasm.exports.sqlite3_create_window_function ? wasm.xWrap("sqlite3_create_window_function", "int", [ + "sqlite3*", + "string", + "int", + "int", + "*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xStep", + ...__cfProxy.xInverseAndStep + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xFinal", + ...__cfProxy.xFinalAndValue + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xValue", + ...__cfProxy.xFinalAndValue + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xInverse", + ...__cfProxy.xInverseAndStep + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xDestroy", + ...__cfProxy.xDestroy + }) + ]) : void 0; + capi.sqlite3_create_function_v2 = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_function_v2", f.length); + else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; + else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); + try { + const rc = __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy); + if (0 === rc && (xFunc instanceof Function || xStep instanceof Function || xFinal instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addFunction(pDb, funcName, nArg); + return rc; + } catch (e) { + console.error("sqlite3_create_function_v2() setup threw:", e); + return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); + } + }; + capi.sqlite3_create_function = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) { + return f.length === arguments.length ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, 0) : __dbArgcMismatch(pDb, "sqlite3_create_function", f.length); + }; + if (__sqlite3CreateWindowFunction) capi.sqlite3_create_window_function = function f(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_window_function", f.length); + else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; + else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); + try { + const rc = __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy); + if (0 === rc && (xStep instanceof Function || xFinal instanceof Function || xValue instanceof Function || xInverse instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); + return rc; + } catch (e) { + console.error("sqlite3_create_window_function() setup threw:", e); + return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); + } + }; + else delete capi.sqlite3_create_window_function; + /** + A _deprecated_ alias for capi.sqlite3_result_js() which + predates the addition of that function in the public API. + */ + capi.sqlite3_create_function_v2.udfSetResult = capi.sqlite3_create_function.udfSetResult = capi.sqlite3_result_js; + if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js; + /** + A _deprecated_ alias for capi.sqlite3_values_to_js() which + predates the addition of that function in the public API. + */ + capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_values_to_js; + if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js; + /** + A _deprecated_ alias for capi.sqlite3_result_error_js() which + predates the addition of that function in the public API. + */ + capi.sqlite3_create_function_v2.udfSetError = capi.sqlite3_create_function.udfSetError = capi.sqlite3_result_error_js; + if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js; + } + { + /** + Helper for string:flexible conversions which requires a + byte-length counterpart argument. Passed a value and its + ostensible length, this function returns [V,N], where V is + either v or a to-string transformed copy of v and N is either n + (if v is a WASM pointer, in which case n might be a BigInt), -1 + (if v is a string or Array), or the byte length of v (if it's a + byte array or ArrayBuffer). + */ + const __flexiString = (v, n) => { + if ("string" === typeof v) n = -1; + else if (util.isSQLableTypedArray(v)) { + n = v.byteLength; + v = wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v); + } else if (Array.isArray(v)) { + v = v.join(""); + n = -1; + } + return [v, n]; + }; + /** + Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). + */ + const __prepare = { + basic: wasm.xWrap("sqlite3_prepare_v3", "int", [ + "sqlite3*", + "string", + "int", + "int", + "**", + "**" + ]), + full: wasm.xWrap("sqlite3_prepare_v3", "int", [ + "sqlite3*", + "*", + "int", + "int", + "**", + "**" + ]) + }; + capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail) { + if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_prepare_v3", f.length); + const [xSql, xSqlLen] = __flexiString(sql, Number(sqlLen)); + switch (typeof xSql) { + case "string": return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); + case typeof wasm.ptr.null: return __prepare.full(pDb, wasm.ptr.coerce(xSql), xSqlLen, prepFlags, ppStmt, pzTail); + default: return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3(). typeof=" + typeof xSql); + } + }; + capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail) { + return f.length === arguments.length ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) : __dbArgcMismatch(pDb, "sqlite3_prepare_v2", f.length); + }; + } + { + const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [ + "sqlite3_stmt*", + "int", + "string", + "int", + "*" + ]); + const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [ + "sqlite3_stmt*", + "int", + "*", + "int", + "*" + ]); + /** Documented in the capi object's initializer. */ + capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_text", f.length); + else if (wasm.isPtr(text) || null === text) return __bindText(pStmt, iCol, text, nText, xDestroy); + else if (text instanceof ArrayBuffer) text = new Uint8Array(text); + else if (Array.isArray(pMem)) text = pMem.join(""); + let p, n; + try { + if (util.isSQLableTypedArray(text)) { + p = wasm.allocFromTypedArray(text); + n = text.byteLength; + } else if ("string" === typeof text) [p, n] = wasm.allocCString(text); + else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_text()."); + return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); + } catch (e) { + wasm.dealloc(p); + return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); + } + }; + /** Documented in the capi object's initializer. */ + capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_blob", f.length); + else if (wasm.isPtr(pMem) || null === pMem) return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy); + else if (pMem instanceof ArrayBuffer) pMem = new Uint8Array(pMem); + else if (Array.isArray(pMem)) pMem = pMem.join(""); + let p, n; + try { + if (util.isBindableTypedArray(pMem)) { + p = wasm.allocFromTypedArray(pMem); + n = nMem >= 0 ? nMem : pMem.byteLength; + } else if ("string" === typeof pMem) [p, n] = wasm.allocCString(pMem); + else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_blob()."); + return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); + } catch (e) { + wasm.dealloc(p); + return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); + } + }; + } + if (!capi.sqlite3_column_text) { + const argStmt = wasm.xWrap.argAdapter("sqlite3_stmt*"), argInt = wasm.xWrap.argAdapter("int"), argValue = wasm.xWrap.argAdapter("sqlite3_value*"), newStr = (cstr, n) => wasm.typedArrayToString(wasm.heap8u(), Number(cstr), Number(cstr) + n); + capi.sqlite3_column_text = function(stmt, colIndex) { + const a0 = argStmt(stmt), a1 = argInt(colIndex); + const cstr = wasm.exports.sqlite3_column_text(a0, a1); + return cstr ? newStr(cstr, wasm.exports.sqlite3_column_bytes(a0, a1)) : null; + }; + capi.sqlite3_value_text = function(val) { + const a0 = argValue(val); + const cstr = wasm.exports.sqlite3_value_text(a0); + return cstr ? newStr(cstr, wasm.exports.sqlite3_value_bytes(a0)) : null; + }; + } + /** + Wraps a small subset of the C API's sqlite3_config() options. + Unsupported options trigger the return of capi.SQLITE_NOTFOUND. + Passing fewer than 2 arguments triggers return of + capi.SQLITE_MISUSE. + */ + capi.sqlite3_config = function(op, ...args) { + if (arguments.length < 2) return capi.SQLITE_MISUSE; + switch (op) { + case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: + case capi.SQLITE_CONFIG_MEMSTATUS: + case capi.SQLITE_CONFIG_SMALL_MALLOC: + case capi.SQLITE_CONFIG_SORTERREF_SIZE: + case capi.SQLITE_CONFIG_STMTJRNL_SPILL: + case capi.SQLITE_CONFIG_URI: return wasm.exports.sqlite3__wasm_config_i(op, args[0]); + case capi.SQLITE_CONFIG_LOOKASIDE: return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]); + case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: return wasm.exports.sqlite3__wasm_config_j(op, args[0]); + case capi.SQLITE_CONFIG_GETMALLOC: + case capi.SQLITE_CONFIG_GETMUTEX: + case capi.SQLITE_CONFIG_GETPCACHE2: + case capi.SQLITE_CONFIG_GETPCACHE: + case capi.SQLITE_CONFIG_HEAP: + case capi.SQLITE_CONFIG_LOG: + case capi.SQLITE_CONFIG_MALLOC: + case capi.SQLITE_CONFIG_MMAP_SIZE: + case capi.SQLITE_CONFIG_MULTITHREAD: + case capi.SQLITE_CONFIG_MUTEX: + case capi.SQLITE_CONFIG_PAGECACHE: + case capi.SQLITE_CONFIG_PCACHE2: + case capi.SQLITE_CONFIG_PCACHE: + case capi.SQLITE_CONFIG_PCACHE_HDRSZ: + case capi.SQLITE_CONFIG_PMASZ: + case capi.SQLITE_CONFIG_SERIALIZED: + case capi.SQLITE_CONFIG_SINGLETHREAD: + case capi.SQLITE_CONFIG_SQLLOG: + case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: + default: return capi.SQLITE_NOTFOUND; + } + }; + { + const __autoExtFptr = /* @__PURE__ */ new Set(); + capi.sqlite3_auto_extension = function(fPtr) { + if (fPtr instanceof Function) fPtr = wasm.installFunction("i(ppp)", fPtr); + else if (1 !== arguments.length || !wasm.isPtr(fPtr)) return capi.SQLITE_MISUSE; + const rc = wasm.exports.sqlite3_auto_extension(fPtr); + if (fPtr !== arguments[0]) if (0 === rc) __autoExtFptr.add(fPtr); + else wasm.uninstallFunction(fPtr); + return rc; + }; + capi.sqlite3_cancel_auto_extension = function(fPtr) { + if (!fPtr || 1 !== arguments.length || !wasm.isPtr(fPtr)) return 0; + return wasm.exports.sqlite3_cancel_auto_extension(fPtr); + }; + capi.sqlite3_reset_auto_extension = function() { + wasm.exports.sqlite3_reset_auto_extension(); + for (const fp of __autoExtFptr) wasm.uninstallFunction(fp); + __autoExtFptr.clear(); + }; + } + wasm.xWrap.FuncPtrAdapter.warnOnUse = true; + const StructBinder = sqlite3.StructBinder; + /** + Installs a StructBinder-bound function pointer member of the + given name and function in the given StructBinder.StructType + target object. + + It creates a WASM proxy for the given function and arranges for + that proxy to be cleaned up when tgt.dispose() is called. Throws + on the slightest hint of error, e.g. tgt is-not-a StructType, + name does not map to a struct-bound member, etc. + + As a special case, if the given function is a pointer, then + `wasm.functionEntry()` is used to validate that it is a known + function. If so, it is used as-is with no extra level of proxying + or cleanup, else an exception is thrown. It is legal to pass a + value of 0, indicating a NULL pointer, with the caveat that 0 + _is_ a legal function pointer in WASM but it will not be accepted + as such _here_. (Justification: the function at address zero must + be one which initially came from the WASM module, not a method we + want to bind to a virtual table or VFS.) + + This function returns a proxy for itself which is bound to tgt + and takes 2 args (name,func). That function returns the same + thing as this one, permitting calls to be chained. + + If called with only 1 arg, it has no side effects but returns a + func with the same signature as described above. + + ACHTUNG: because we cannot generically know how to transform JS + exceptions into result codes, the installed functions do no + automatic catching of exceptions. It is critical, to avoid + undefined behavior in the C layer, that methods mapped via + this function do not throw. The exception, as it were, to that + rule is... + + If applyArgcCheck is true then each JS function (as opposed to + function pointers) gets wrapped in a proxy which asserts that it + is passed the expected number of arguments, throwing if the + argument count does not match expectations. That is only intended + for dev-time usage for sanity checking, and may leave the C + environment in an undefined state. + */ + const installMethod = function callee(tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck) { + if (!(tgt instanceof StructBinder.StructType)) toss("Usage error: target object is-not-a StructType."); + else if (!(func instanceof Function) && !wasm.isPtr(func)) toss("Usage error: expecting a Function or WASM pointer to one."); + if (1 === arguments.length) return (n, f) => callee(tgt, n, f, applyArgcCheck); + if (!callee.argcProxy) { + callee.argcProxy = function(tgt, funcName, func, sig) { + return function(...args) { + if (func.length !== arguments.length) toss("Argument mismatch for", tgt.structInfo.name + "::" + funcName + ": Native signature is:", sig); + return func.apply(this, args); + }; + }; + callee.removeFuncList = function() { + if (this.ondispose.__removeFuncList) { + this.ondispose.__removeFuncList.forEach((v, ndx) => { + if (wasm.isPtr(v)) try { + wasm.uninstallFunction(v); + } catch (e) {} + }); + delete this.ondispose.__removeFuncList; + } + }; + } + const sigN = tgt.memberSignature(name); + if (sigN.length < 2) toss("Member", name, "does not have a function pointer signature:", sigN); + const memKey = tgt.memberKey(name); + const fProxy = applyArgcCheck && !wasm.isPtr(func) ? callee.argcProxy(tgt, memKey, func, sigN) : func; + if (wasm.isPtr(fProxy)) { + if (fProxy && !wasm.functionEntry(fProxy)) toss("Pointer", fProxy, "is not a WASM function table entry."); + tgt[memKey] = fProxy; + } else { + const pFunc = wasm.installFunction(fProxy, sigN); + tgt[memKey] = pFunc; + if (!tgt.ondispose || !tgt.ondispose.__removeFuncList) { + tgt.addOnDispose("ondispose.__removeFuncList handler", callee.removeFuncList); + tgt.ondispose.__removeFuncList = []; + } + tgt.ondispose.__removeFuncList.push(memKey, pFunc); + } + return (n, f) => callee(tgt, n, f, applyArgcCheck); + }; + installMethod.installMethodArgcCheck = false; + /** + Installs methods into the given StructBinder.StructType-type + instance. Each entry in the given methods object must map to a + known member of the given StructType, else an exception will be + triggered. See installMethod() for more details, including the + semantics of the 3rd argument. + + As an exception to the above, if any two or more methods in the + 2nd argument are the exact same function, installMethod() is + _not_ called for the 2nd and subsequent instances, and instead + those instances get assigned the same method pointer which is + created for the first instance. This optimization is primarily to + accommodate special handling of sqlite3_module::xConnect and + xCreate methods. + + On success, returns its first argument. Throws on error. + */ + const installMethods = function(structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck) { + const seen = /* @__PURE__ */ new Map(); + for (const k of Object.keys(methods)) { + const m = methods[k]; + const prior = seen.get(m); + if (prior) { + const mkey = structInstance.memberKey(k); + structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; + } else { + installMethod(structInstance, k, m, applyArgcCheck); + seen.set(m, k); + } + } + return structInstance; + }; + /** + Equivalent to calling installMethod(this,...arguments) with a + first argument of this object. If called with 1 or 2 arguments + and the first is an object, it's instead equivalent to calling + installMethods(this,...arguments). + */ + StructBinder.StructType.prototype.installMethod = function callee(name, func, applyArgcCheck = installMethod.installMethodArgcCheck) { + return arguments.length < 3 && name && "object" === typeof name ? installMethods(this, ...arguments) : installMethod(this, ...arguments); + }; + /** + Equivalent to calling installMethods() with a first argument + of this object. + */ + StructBinder.StructType.prototype.installMethods = function(methods, applyArgcCheck = installMethod.installMethodArgcCheck) { + return installMethods(this, methods, applyArgcCheck); + }; + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + const toss3 = (...args) => { + throw new sqlite3.SQLite3Error(...args); + }; + const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; + const outWrapper = function(f) { + return (...args) => f("sqlite3.oo1:", ...args); + }; + sqlite3.__isUnderTest ? outWrapper(console.debug.bind(console)) : outWrapper(sqlite3.config.debug); + sqlite3.__isUnderTest ? outWrapper(console.warn.bind(console)) : outWrapper(sqlite3.config.warn); + sqlite3.__isUnderTest ? outWrapper(console.error.bind(console)) : outWrapper(sqlite3.config.error); + /** + In order to keep clients from manipulating, perhaps + inadvertently, the underlying pointer values of DB and Stmt + instances, we'll gate access to them via the `pointer` property + accessor and store their real values in this map. Keys = DB/Stmt + objects, values = pointer values. This also unifies how those are + accessed, for potential use downstream via custom + wasm.xWrap() function signatures which know how to extract + it. + */ + const __ptrMap = /* @__PURE__ */ new WeakMap(); + /** + A Set of oo1.DB or oo1.Stmt objects which are proxies for + (sqlite3*) resp. (sqlite3_stmt*) pointers which themselves are + owned elsewhere. Objects in this Set do not own their underlying + handle and that handle must be guaranteed (by the client) to + outlive the proxy. DB.close()/Stmt.finalize() methods will remove + the object from this Set _instead_ of closing/finalizing the + pointer. These proxies are primarily intended as a way to briefly + wrap an (sqlite3[_stmt]*) object as an oo1.DB/Stmt without taking + over ownership, to take advantage of simplifies usage compared to + the C API while not imposing any change of ownership. + + See DB.wrapHandle() and Stmt.wrapHandle(). + */ + const __doesNotOwnHandle = /* @__PURE__ */ new Set(); + /** + Map of DB instances to objects, each object being a map of Stmt + wasm pointers to Stmt objects. + */ + const __stmtMap = /* @__PURE__ */ new WeakMap(); + /** If object opts has _its own_ property named p then that + property's value is returned, else dflt is returned. */ + const getOwnOption = (opts, p, dflt) => { + const d = Object.getOwnPropertyDescriptor(opts, p); + return d ? d.value : dflt; + }; + const checkSqlite3Rc = function(dbPtr, sqliteResultCode) { + if (sqliteResultCode) { + if (dbPtr instanceof DB) dbPtr = dbPtr.pointer; + toss3(sqliteResultCode, "sqlite3 result code", sqliteResultCode + ":", dbPtr ? capi.sqlite3_errmsg(dbPtr) : capi.sqlite3_errstr(sqliteResultCode)); + } + return arguments[0]; + }; + /** + sqlite3_trace_v2() callback which gets installed by the DB ctor + if its open-flags contain "t". + */ + const __dbTraceToConsole = wasm.installFunction("i(ippp)", function(t, c, p, x) { + if (capi.SQLITE_TRACE_STMT === t) console.log("SQL TRACE #" + ++this.counter, "via sqlite3@" + c + "[" + capi.sqlite3_db_filename(c, null) + "]", wasm.cstrToJs(x)); + }.bind({ counter: 0 })); + /** + A map of sqlite3_vfs pointers to SQL code or a callback function + to run when the DB constructor opens a database with the given + VFS. In the latter case, the call signature is + (theDbObject,sqlite3Namespace) and the callback is expected to + throw on error. + */ + const __vfsPostOpenCallback = Object.create(null); + /** + A proxy for DB class constructors. It must be called with the + being-construct DB object as its "this". See the DB constructor + for the argument docs. This is split into a separate function + in order to enable simple creation of special-case DB constructors, + e.g. JsStorageDb and OpfsDb. + + Expects to be passed a configuration object with the following + properties: + + - `.filename`: the db filename. It may be a special name like ":memory:" + or "". It may also be a URI-style name. + + - `.flags`: as documented in the DB constructor. + + - `.vfs`: as documented in the DB constructor. + + It also accepts those as the first 3 arguments. + + In non-default builds it may accept additional configuration + options. + */ + const dbCtorHelper = function ctor(...args) { + const opt = ctor.normalizeArgs(...args); + let pDb; + if (pDb = opt["sqlite3*"]) { + if (!opt["sqlite3*:takeOwnership"]) __doesNotOwnHandle.add(this); + this.filename = capi.sqlite3_db_filename(pDb, "main"); + } else { + let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags; + if ("string" !== typeof fn && !wasm.isPtr(fn) || "string" !== typeof flagsStr || vfsName && "string" !== typeof vfsName && !wasm.isPtr(vfsName)) { + sqlite3.config.error("Invalid DB ctor args", opt, arguments); + toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt); + } + let oflags = 0; + if (flagsStr.indexOf("c") >= 0) oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + if (flagsStr.indexOf("w") >= 0) oflags |= capi.SQLITE_OPEN_READWRITE; + if (0 === oflags) oflags |= capi.SQLITE_OPEN_READONLY; + oflags |= capi.SQLITE_OPEN_EXRESCODE; + const stack = wasm.pstack.pointer; + try { + const pPtr = wasm.pstack.allocPtr(); + let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || wasm.ptr.null); + pDb = wasm.peekPtr(pPtr); + checkSqlite3Rc(pDb, rc); + capi.sqlite3_extended_result_codes(pDb, 1); + if (flagsStr.indexOf("t") >= 0) capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, pDb); + } catch (e) { + if (pDb) capi.sqlite3_close_v2(pDb); + throw e; + } finally { + wasm.pstack.restore(stack); + } + this.filename = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn; + } + __ptrMap.set(this, pDb); + __stmtMap.set(this, Object.create(null)); + if (!opt["sqlite3*"]) try { + const postInitSql = __vfsPostOpenCallback[capi.sqlite3_js_db_vfs(pDb) || toss3("Internal error: cannot get VFS for new db handle.")]; + if (postInitSql) + /** + Reminder: if this db is encrypted and the client did _not_ pass + in the key, any init code will fail, causing the ctor to throw. + We don't actually know whether the db is encrypted, so we cannot + sensibly apply any heuristics which skip the init code only for + encrypted databases for which no key has yet been supplied. + */ + if (postInitSql instanceof Function) postInitSql(this, sqlite3); + else checkSqlite3Rc(pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)); + } catch (e) { + this.close(); + throw e; + } + }; + /** + Sets a callback which should be called after a db is opened with + the given sqlite3_vfs pointer. The 2nd argument must be a + function, which gets called with + (theOo1DbObject,sqlite3Namespace) at the end of the DB() + constructor. The function must throw on error, in which case the + db is closed and the exception is propagated. This function is + intended only for use by DB subclasses or sqlite3_vfs + implementations. + + Prior to 2024-07-22, it was legal to pass SQL code as the second + argument, but that can interfere with a client's ability to run + pragmas which must be run before anything else, namely (pragma + locking_mode=exclusive) for use with WAL mode. That capability + had only ever been used as an internal detail of the two OPFS + VFSes, and they no longer use it that way. + */ + dbCtorHelper.setVfsPostOpenCallback = function(pVfs, callback) { + if (!(callback instanceof Function)) toss3("dbCtorHelper.setVfsPostOpenCallback() should not be used with a non-function argument.", arguments); + __vfsPostOpenCallback[pVfs] = callback; + }; + /** + A helper for DB constructors. It accepts either a single + config-style object or up to 3 arguments (filename, dbOpenFlags, + dbVfsName). It returns a new object containing: + + { filename: ..., flags: ..., vfs: ... } + + If passed an object, any additional properties it has are copied + as-is into the new object. + */ + dbCtorHelper.normalizeArgs = function(filename = ":memory:", flags = "c", vfs = null) { + const arg = {}; + if (1 === arguments.length && arguments[0] && "object" === typeof arguments[0]) { + Object.assign(arg, arguments[0]); + if (void 0 === arg.flags) arg.flags = "c"; + if (void 0 === arg.vfs) arg.vfs = null; + if (void 0 === arg.filename) arg.filename = ":memory:"; + } else { + arg.filename = filename; + arg.flags = flags; + arg.vfs = vfs; + } + return arg; + }; + /** + The DB class provides a high-level OO wrapper around an sqlite3 + db handle. + + The given db filename must be resolvable using whatever + filesystem layer (virtual or otherwise) is set up for the default + sqlite3 VFS or a VFS which can resolve it must be specified. + + The special sqlite3 db names ":memory:" and "" (temporary db) + have their normal special meanings here and need not resolve to + real filenames, but "" uses an on-storage temporary database and + requires that the VFS support that. + + The second argument specifies the open/create mode for the + database. It must be string containing a sequence of letters (in + any order, but case sensitive) specifying the mode: + + - "c": create if it does not exist, else fail if it does not + exist. Implies the "w" flag. + + - "w": write. Implies "r": a db cannot be write-only. + + - "r": read-only if neither "w" nor "c" are provided, else it + is ignored. + + - "t": enable tracing of SQL executed on this database handle, + sending it to `console.log()`. To disable it later, call + `sqlite3.capi.sqlite3_trace_v2(thisDb.pointer, 0, 0, 0)`. + + If "w" is not provided, the db is implicitly read-only, noting + that "rc" is meaningless + + Any other letters are currently ignored. The default is + "c". These modes are ignored for the special ":memory:" and "" + names and _may_ be ignored altogether for certain VFSes. + + The final argument is analogous to the final argument of + sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value, + or none at all, to use the default. If passed a value, it must + be the string name of a VFS. + + The constructor optionally (and preferably) takes its arguments + in the form of a single configuration object with the following + properties: + + - `filename`: database file name + - `flags`: open-mode flags + - `vfs`: the VFS fname + + + The `filename` and `vfs` arguments may be either JS strings or + C-strings allocated via WASM. `flags` is required to be a JS + string (because it's specific to this API, which is specific + to JS). + + For purposes of passing a DB instance to C-style sqlite3 + functions, the DB object's read-only `pointer` property holds its + `sqlite3*` pointer value. That property can also be used to check + whether this DB instance is still open: it will evaluate to + `undefined` after the DB object's close() method is called. + + In the main window thread, the filenames `":localStorage:"` and + `":sessionStorage:"` are special: they cause the db to use either + localStorage or sessionStorage for storing the database using + the kvvfs. If one of these names are used, they trump + any vfs name set in the arguments. + */ + const DB = function(...args) { + dbCtorHelper.apply(this, args); + }; + DB.dbCtorHelper = dbCtorHelper; + /** + Internal-use enum for mapping JS types to DB-bindable types. + These do not (and need not) line up with the SQLITE_type + values. All values in this enum must be truthy and (mostly) + distinct but they need not be numbers. + */ + const BindTypes = { + null: 1, + number: 2, + string: 3, + boolean: 4, + blob: 5 + }; + if (wasm.bigIntEnabled) BindTypes.bigint = BindTypes.number; + /** + This class wraps sqlite3_stmt. Calling this constructor + directly will trigger an exception. Use DB.prepare() to create + new instances. + + For purposes of passing a Stmt instance to C-style sqlite3 + functions, its read-only `pointer` property holds its `sqlite3_stmt*` + pointer value. + + Other non-function properties include: + + - `db`: the DB object which created the statement. + + - `columnCount`: the number of result columns in the query, or 0 + for queries which cannot return results. This property is a + read-only proxy for sqlite3_column_count() and its use in loops + should be avoided because of the call overhead associated with + that. The `columnCount` is not cached when the Stmt is created + because a schema change made between this statement's preparation + and when it is stepped may invalidate it. + + - `parameterCount`: the number of bindable parameters in the + query. Like `columnCount`, this property is ready-only and is a + proxy for a C API call. + + As a general rule, most methods of this class will throw if + called on an instance which has been finalized. For brevity's + sake, the method docs do not all repeat this warning. + */ + const Stmt = function() { + if (BindTypes !== arguments[2]) toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare()."); + this.db = arguments[0]; + __ptrMap.set(this, arguments[1]); + if (arguments.length > 3 && !arguments[3]) __doesNotOwnHandle.add(this); + }; + /** Throws if the given DB has been closed, else it is returned. */ + const affirmDbOpen = function(db) { + if (!db.pointer) toss3("DB has been closed."); + return db; + }; + /** Throws if ndx is not an integer or if it is out of range + for stmt.columnCount, else returns stmt. + + Reminder: this will also fail after the statement is finalized + but the resulting error will be about an out-of-bounds column + index rather than a statement-is-finalized error. + */ + const affirmColIndex = function(stmt, ndx) { + if (ndx !== (ndx | 0) || ndx < 0 || ndx >= stmt.columnCount) toss3("Column index", ndx, "is out of range."); + return stmt; + }; + /** + Expects to be passed the `arguments` object from DB.exec(). Does + the argument processing/validation, throws on error, and returns + a new object on success: + + { sql: the SQL, opt: optionsObj, cbArg: function} + + The opt object is a normalized copy of any passed to this + function. The sql will be converted to a string if it is provided + in one of the supported non-string formats. + + cbArg is only set if the opt.callback or opt.resultRows are set, + in which case it's a function which expects to be passed the + current Stmt and returns the callback argument of the type + indicated by the input arguments. + */ + const parseExecArgs = function(db, args) { + const out = Object.create(null); + out.opt = Object.create(null); + switch (args.length) { + case 1: + if ("string" === typeof args[0] || util.isSQLableTypedArray(args[0])) out.sql = args[0]; + else if (Array.isArray(args[0])) out.sql = args[0]; + else if (args[0] && "object" === typeof args[0]) { + out.opt = args[0]; + out.sql = out.opt.sql; + } + break; + case 2: + out.sql = args[0]; + out.opt = args[1]; + break; + default: toss3("Invalid argument count for exec()."); + } + out.sql = util.flexibleString(out.sql); + if ("string" !== typeof out.sql) toss3("Missing SQL argument or unsupported SQL value type."); + const opt = out.opt; + switch (opt.returnValue) { + case "resultRows": + if (!opt.resultRows) opt.resultRows = []; + out.returnVal = () => opt.resultRows; + break; + case "saveSql": + if (!opt.saveSql) opt.saveSql = []; + out.returnVal = () => opt.saveSql; + break; + case void 0: + case "this": + out.returnVal = () => db; + break; + default: toss3("Invalid returnValue value:", opt.returnValue); + } + if (!opt.callback && !opt.returnValue && void 0 !== opt.rowMode) { + if (!opt.resultRows) opt.resultRows = []; + out.returnVal = () => opt.resultRows; + } + if (opt.callback || opt.resultRows) switch (void 0 === opt.rowMode ? "array" : opt.rowMode) { + case "object": + out.cbArg = (stmt, cache) => { + if (!cache.columnNames) cache.columnNames = stmt.getColumnNames([]); + const row = stmt.get([]); + const rv = Object.create(null); + for (const i in cache.columnNames) rv[cache.columnNames[i]] = row[i]; + return rv; + }; + break; + case "array": + out.cbArg = (stmt) => stmt.get([]); + break; + case "stmt": + if (Array.isArray(opt.resultRows)) toss3("exec(): invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", "a result column number, or column name reference."); + out.cbArg = (stmt) => stmt; + break; + default: + if (util.isInt32(opt.rowMode)) { + out.cbArg = (stmt) => stmt.get(opt.rowMode); + break; + } else if ("string" === typeof opt.rowMode && opt.rowMode.length > 1 && "$" === opt.rowMode[0]) { + const $colName = opt.rowMode.substr(1); + out.cbArg = (stmt) => { + const rc = stmt.get(Object.create(null))[$colName]; + return void 0 === rc ? toss3(capi.SQLITE_NOTFOUND, "exec(): unknown result column:", $colName) : rc; + }; + break; + } + toss3("Invalid rowMode:", opt.rowMode); + } + return out; + }; + /** + Internal impl of the DB.selectValue(), selectArray(), and + selectObject() methods. + */ + const __selectFirstRow = (db, sql, bind, ...getArgs) => { + const stmt = db.prepare(sql); + try { + const rc = stmt.bind(bind).step() ? stmt.get(...getArgs) : void 0; + stmt.reset(); + return rc; + } finally { + stmt.finalize(); + } + }; + /** + Internal impl of the DB.selectArrays() and selectObjects() + methods. + */ + const __selectAll = (db, sql, bind, rowMode) => db.exec({ + sql, + bind, + rowMode, + returnValue: "resultRows" + }); + /** + Expects to be given a DB instance or an `sqlite3*` pointer (may + be null) and an sqlite3 API result code. If the result code is + not falsy, this function throws an SQLite3Error with an error + message from sqlite3_errmsg(), using db (or, if db is-a DB, + db.pointer) as the db handle, or sqlite3_errstr() if db is + falsy. Note that if it's passed a non-error code like SQLITE_ROW + or SQLITE_DONE, it will still throw but the error string might be + "Not an error." The various non-0 non-error codes need to be + checked for in client code where they are expected. + + The thrown exception's `resultCode` property will be the value of + the second argument to this function. + + If it does not throw, it returns its first argument. + */ + DB.checkRc = (db, resultCode) => checkSqlite3Rc(db, resultCode); + DB.prototype = { + isOpen: function() { + return !!this.pointer; + }, + affirmOpen: function() { + return affirmDbOpen(this); + }, + close: function() { + const pDb = this.pointer; + if (pDb) { + if (this.onclose && this.onclose.before instanceof Function) try { + this.onclose.before(this); + } catch (e) {} + Object.keys(__stmtMap.get(this)).forEach((k, s) => { + if (s && s.pointer) try { + s.finalize(); + } catch (e) {} + }); + __ptrMap.delete(this); + __stmtMap.delete(this); + if (!__doesNotOwnHandle.delete(this)) capi.sqlite3_close_v2(pDb); + if (this.onclose && this.onclose.after instanceof Function) try { + this.onclose.after(this); + } catch (e) {} + delete this.filename; + } + }, + changes: function(total = false, sixtyFour = false) { + const p = affirmDbOpen(this).pointer; + if (total) return sixtyFour ? capi.sqlite3_total_changes64(p) : capi.sqlite3_total_changes(p); + else return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p); + }, + dbFilename: function(dbName = "main") { + return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); + }, + dbName: function(dbNumber = 0) { + return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); + }, + dbVfsName: function(dbName = 0) { + let rc; + const pVfs = capi.sqlite3_js_db_vfs(affirmDbOpen(this).pointer, dbName); + if (pVfs) { + const v = new capi.sqlite3_vfs(pVfs); + try { + rc = wasm.cstrToJs(v.$zName); + } finally { + v.dispose(); + } + } + return rc; + }, + prepare: function(sql) { + affirmDbOpen(this); + const stack = wasm.pstack.pointer; + let ppStmt, pStmt; + try { + ppStmt = wasm.pstack.alloc(8); + DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); + pStmt = wasm.peekPtr(ppStmt); + } finally { + wasm.pstack.restore(stack); + } + if (!pStmt) toss3("Cannot prepare empty SQL."); + const stmt = new Stmt(this, pStmt, BindTypes); + __stmtMap.get(this)[pStmt] = stmt; + return stmt; + }, + exec: function() { + affirmDbOpen(this); + const arg = parseExecArgs(this, arguments); + if (!arg.sql) return toss3("exec() requires an SQL string."); + const opt = arg.opt; + const callback = opt.callback; + const resultRows = Array.isArray(opt.resultRows) ? opt.resultRows : void 0; + let stmt; + let bind = opt.bind; + let evalFirstResult = !!(arg.cbArg || opt.columnNames || resultRows); + const stack = wasm.scopedAllocPush(); + const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : void 0; + try { + const isTA = util.isSQLableTypedArray(arg.sql); + let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); + const ppStmt = wasm.scopedAlloc(2 * wasm.ptr.size + (sqlByteLen + 1)); + const pzTail = wasm.ptr.add(ppStmt, wasm.ptr.size); + let pSql = wasm.ptr.add(pzTail, wasm.ptr.size); + const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen); + if (isTA) wasm.heap8().set(arg.sql, pSql); + else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); + wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0); + while (pSql && wasm.peek8(pSql)) { + wasm.pokePtr([ppStmt, pzTail], 0); + DB.checkRc(this, capi.sqlite3_prepare_v3(this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail)); + const pStmt = wasm.peekPtr(ppStmt); + pSql = wasm.peekPtr(pzTail); + sqlByteLen = Number(wasm.ptr.add(pSqlEnd, -pSql)); + if (!pStmt) continue; + if (saveSql) saveSql.push(capi.sqlite3_sql(pStmt).trim()); + stmt = new Stmt(this, pStmt, BindTypes); + if (bind && stmt.parameterCount) { + stmt.bind(bind); + bind = null; + } + if (evalFirstResult && stmt.columnCount) { + let gotColNames = Array.isArray(opt.columnNames) ? 0 : 1; + evalFirstResult = false; + if (arg.cbArg || resultRows) { + const cbArgCache = Object.create(null); + for (; stmt.step(); __execLock.delete(stmt)) { + if (0 === gotColNames++) stmt.getColumnNames(cbArgCache.columnNames = opt.columnNames || []); + __execLock.add(stmt); + const row = arg.cbArg(stmt, cbArgCache); + if (resultRows) resultRows.push(row); + if (callback && false === callback.call(opt, row, stmt)) break; + } + __execLock.delete(stmt); + } + if (0 === gotColNames) stmt.getColumnNames(opt.columnNames); + } else stmt.step(); + stmt.reset().finalize(); + stmt = null; + } + } finally { + if (stmt) { + __execLock.delete(stmt); + stmt.finalize(); + } + wasm.scopedAllocPop(stack); + } + return arg.returnVal(); + }, + createFunction: function f(name, xFunc, opt) { + const isFunc = (f) => f instanceof Function; + switch (arguments.length) { + case 1: + opt = name; + name = opt.name; + xFunc = opt.xFunc || 0; + break; + case 2: + if (!isFunc(xFunc)) { + opt = xFunc; + xFunc = opt.xFunc || 0; + } + break; + case 3: break; + default: break; + } + if (!opt) opt = {}; + if ("string" !== typeof name) toss3("Invalid arguments: missing function name."); + let xStep = opt.xStep || 0; + let xFinal = opt.xFinal || 0; + const xValue = opt.xValue || 0; + const xInverse = opt.xInverse || 0; + let isWindow = void 0; + if (isFunc(xFunc)) { + isWindow = false; + if (isFunc(xStep) || isFunc(xFinal)) toss3("Ambiguous arguments: scalar or aggregate?"); + xStep = xFinal = null; + } else if (isFunc(xStep)) { + if (!isFunc(xFinal)) toss3("Missing xFinal() callback for aggregate or window UDF."); + xFunc = null; + } else if (isFunc(xFinal)) toss3("Missing xStep() callback for aggregate or window UDF."); + else toss3("Missing function-type properties."); + if (false === isWindow) { + if (isFunc(xValue) || isFunc(xInverse)) toss3("xValue and xInverse are not permitted for non-window UDFs."); + } else if (isFunc(xValue)) { + if (!isFunc(xInverse)) toss3("xInverse must be provided if xValue is."); + isWindow = true; + } else if (isFunc(xInverse)) toss3("xValue must be provided if xInverse is."); + const pApp = opt.pApp; + if (void 0 !== pApp && null !== pApp && !wasm.isPtr(pApp)) toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); + const xDestroy = opt.xDestroy || 0; + if (xDestroy && !isFunc(xDestroy)) toss3("xDestroy property must be a function."); + let fFlags = 0; + if (getOwnOption(opt, "deterministic")) fFlags |= capi.SQLITE_DETERMINISTIC; + if (getOwnOption(opt, "directOnly")) fFlags |= capi.SQLITE_DIRECTONLY; + if (getOwnOption(opt, "innocuous")) fFlags |= capi.SQLITE_INNOCUOUS; + name = name.toLowerCase(); + const xArity = xFunc || xStep; + const arity = getOwnOption(opt, "arity"); + const arityArg = "number" === typeof arity ? arity : xArity.length ? xArity.length - 1 : 0; + let rc; + if (isWindow) rc = capi.sqlite3_create_window_function(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xStep, xFinal, xValue, xInverse, xDestroy); + else rc = capi.sqlite3_create_function_v2(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xFunc, xStep, xFinal, xDestroy); + DB.checkRc(this, rc); + return this; + }, + selectValue: function(sql, bind, asType) { + return __selectFirstRow(this, sql, bind, 0, asType); + }, + selectValues: function(sql, bind, asType) { + const stmt = this.prepare(sql), rc = []; + try { + stmt.bind(bind); + while (stmt.step()) rc.push(stmt.get(0, asType)); + stmt.reset(); + } finally { + stmt.finalize(); + } + return rc; + }, + selectArray: function(sql, bind) { + return __selectFirstRow(this, sql, bind, []); + }, + selectObject: function(sql, bind) { + return __selectFirstRow(this, sql, bind, {}); + }, + selectArrays: function(sql, bind) { + return __selectAll(this, sql, bind, "array"); + }, + selectObjects: function(sql, bind) { + return __selectAll(this, sql, bind, "object"); + }, + openStatementCount: function() { + return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; + }, + transaction: function(callback) { + let opener = "BEGIN"; + if (arguments.length > 1) { + if (/[^a-zA-Z]/.test(arguments[0])) toss3(capi.SQLITE_MISUSE, "Invalid argument for BEGIN qualifier."); + opener += " " + arguments[0]; + callback = arguments[1]; + } + affirmDbOpen(this).exec(opener); + try { + const rc = callback(this); + this.exec("COMMIT"); + return rc; + } catch (e) { + this.exec("ROLLBACK"); + throw e; + } + }, + savepoint: function(callback) { + affirmDbOpen(this).exec("SAVEPOINT oo1"); + try { + const rc = callback(this); + this.exec("RELEASE oo1"); + return rc; + } catch (e) { + this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); + throw e; + } + }, + checkRc: function(resultCode) { + return checkSqlite3Rc(this, resultCode); + } + }; + /** + Returns a new oo1.DB instance which wraps the given (sqlite3*) + WASM pointer, optionally with or without taking over ownership of + that pointer. + + The first argument must be either a non-NULL (sqlite3*) WASM + pointer. + + The second argument, defaulting to false, specifies ownership of + the first argument. If it is truthy, the returned object will + pass that pointer to sqlite3_close() when its close() method is + called, otherwise it will not. + + Throws if pDb is not a non-0 WASM pointer. + + The caller MUST GUARANTEE that the passed-in handle will outlive + the returned object, i.e. that it will not be closed. If it is closed, + this object will hold a stale pointer and results are undefined. + + Aside from its lifetime, the proxy is to be treated as any other + DB instance, including the requirement of calling close() on + it. close() will free up internal resources owned by the proxy + and disassociate the proxy from that handle but will not + actually close the proxied db handle unless this function is + passed a thruthy second argument. + + To stress: + + - DO NOT call sqlite3_close() (or similar) on the being-proxied + pointer while a proxy is active. + + - ALWAYS eventually call close() on the returned object. If the + proxy does not own the underlying handle then its MUST be + closed BEFORE the being-proxied handle is closed. + + Design notes: + + - wrapHandle() "could" accept a DB object instance as its first + argument and proxy thatDb.pointer but there is currently no use + case where doing so would be useful, so it does not allow + that. That restriction may be lifted in a future version. + */ + DB.wrapHandle = function(pDb, takeOwnership = false) { + if (!pDb || !wasm.isPtr(pDb)) throw new sqlite3.SQLite3Error(capi.SQLITE_MISUSE, "Argument must be a WASM sqlite3 pointer"); + return new DB({ + "sqlite3*": pDb, + "sqlite3*:takeOwnership": !!takeOwnership + }); + }; + /** Throws if the given Stmt has been finalized, else stmt is + returned. */ + const affirmStmtOpen = function(stmt) { + if (!stmt.pointer) toss3("Stmt has been closed."); + return stmt; + }; + /** Returns an opaque truthy value from the BindTypes + enum if v's type is a valid bindable type, else + returns a falsy value. As a special case, a value of + undefined is treated as a bind type of null. */ + const isSupportedBindType = function(v) { + let t = BindTypes[null === v || void 0 === v ? "null" : typeof v]; + switch (t) { + case BindTypes.boolean: + case BindTypes.null: + case BindTypes.number: + case BindTypes.string: return t; + case BindTypes.bigint: return wasm.bigIntEnabled ? t : void 0; + default: return util.isBindableTypedArray(v) ? BindTypes.blob : void 0; + } + }; + /** + If isSupportedBindType(v) returns a truthy value, this + function returns that value, else it throws. + */ + const affirmSupportedBindType = function(v) { + return isSupportedBindType(v) || toss3("Unsupported bind() argument type:", typeof v); + }; + /** + If key is a number and within range of stmt's bound parameter + count, key is returned. + + If key is not a number then it must be a JS string (not a WASM + string) and it is checked against named parameters. If a match is + found, its index is returned. + + Else it throws. + */ + const affirmParamIndex = function(stmt, key) { + const n = "number" === typeof key ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); + if (0 === n || !util.isInt32(n)) toss3("Invalid bind() parameter name: " + key); + else if (n < 1 || n > stmt.parameterCount) toss3("Bind index", key, "is out of range."); + return n; + }; + /** + Each Stmt object which is "locked" by DB.exec() gets an entry + here to note that "lock". + + The reason this is in place is because exec({callback:...})'s + callback gets access to the Stmt objects created internally by + exec() but it must not use certain Stmt APIs. + */ + const __execLock = /* @__PURE__ */ new Set(); + /** + This is a Stmt.get() counterpart of __execLock. Each time + Stmt.step() returns true, the statement is added to this set, + indicating that Stmt.get() is legal. Stmt APIs which invalidate + that status remove the Stmt object from this set, which will + cause Stmt.get() to throw with a descriptive error message + instead of a more generic "API misuse" if we were to allow that + call to reach the C API. + */ + const __stmtMayGet = /* @__PURE__ */ new Set(); + /** + Stmt APIs which are prohibited on locked objects must call + affirmNotLockedByExec() before doing any work. + + If __execLock.has(stmt) is truthy, this throws an exception + complaining that the 2nd argument (an operation name, + e.g. "bind()") is not legal while the statement is "locked". + Locking happens before an exec()-like callback is passed a + statement, to ensure that the callback does not mutate or + finalize the statement. If it does not throw, it returns stmt. + */ + const affirmNotLockedByExec = function(stmt, currentOpName) { + if (__execLock.has(stmt)) toss3("Operation is illegal when statement is locked:", currentOpName); + return stmt; + }; + /** + Binds a single bound parameter value on the given stmt at the + given index (numeric or named) using the given bindType (see + the BindTypes enum) and value. Throws on error. Returns stmt on + success. + */ + const bindOne = function f(stmt, ndx, bindType, val) { + affirmNotLockedByExec(affirmStmtOpen(stmt), "bind()"); + if (!f._) { + f._tooBigInt = (v) => toss3("BigInt value is too big to store without precision loss:", v); + f._ = { string: function(stmt, ndx, val, asBlob) { + const [pStr, n] = wasm.allocCString(val, true); + return (asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text)(stmt.pointer, ndx, pStr, n, capi.SQLITE_WASM_DEALLOC); + } }; + } + affirmSupportedBindType(val); + ndx = affirmParamIndex(stmt, ndx); + let rc = 0; + switch (null === val || void 0 === val ? BindTypes.null : bindType) { + case BindTypes.null: + rc = capi.sqlite3_bind_null(stmt.pointer, ndx); + break; + case BindTypes.string: + rc = f._.string(stmt, ndx, val, false); + break; + case BindTypes.number: { + let m; + if (util.isInt32(val)) m = capi.sqlite3_bind_int; + else if ("bigint" === typeof val) if (!util.bigIntFits64(val)) f._tooBigInt(val); + else if (wasm.bigIntEnabled) m = capi.sqlite3_bind_int64; + else if (util.bigIntFitsDouble(val)) { + val = Number(val); + m = capi.sqlite3_bind_double; + } else f._tooBigInt(val); + else { + val = Number(val); + if (wasm.bigIntEnabled && Number.isInteger(val)) m = capi.sqlite3_bind_int64; + else m = capi.sqlite3_bind_double; + } + rc = m(stmt.pointer, ndx, val); + break; + } + case BindTypes.boolean: + rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); + break; + case BindTypes.blob: { + if ("string" === typeof val) { + rc = f._.string(stmt, ndx, val, true); + break; + } else if (val instanceof ArrayBuffer) val = new Uint8Array(val); + else if (!util.isBindableTypedArray(val)) toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, Int8Array, or ArrayBuffer."); + const pBlob = wasm.alloc(val.byteLength || 1); + wasm.heap8().set(val.byteLength ? val : [0], Number(pBlob)); + rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); + break; + } + default: + sqlite3.config.warn("Unsupported bind() argument type:", val); + toss3("Unsupported bind() argument type: " + typeof val); + } + if (rc) DB.checkRc(stmt.db.pointer, rc); + return stmt; + }; + Stmt.prototype = { + finalize: function() { + const ptr = this.pointer; + if (ptr) { + affirmNotLockedByExec(this, "finalize()"); + const rc = __doesNotOwnHandle.delete(this) ? 0 : capi.sqlite3_finalize(ptr); + delete __stmtMap.get(this.db)[ptr]; + __ptrMap.delete(this); + __execLock.delete(this); + __stmtMayGet.delete(this); + delete this.parameterCount; + delete this.db; + return rc; + } + }, + clearBindings: function() { + affirmNotLockedByExec(affirmStmtOpen(this), "clearBindings()"); + capi.sqlite3_clear_bindings(this.pointer); + __stmtMayGet.delete(this); + return this; + }, + reset: function(alsoClearBinds) { + affirmNotLockedByExec(this, "reset()"); + if (alsoClearBinds) this.clearBindings(); + const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer); + __stmtMayGet.delete(this); + checkSqlite3Rc(this.db, rc); + return this; + }, + bind: function() { + affirmStmtOpen(this); + let ndx, arg; + switch (arguments.length) { + case 1: + ndx = 1; + arg = arguments[0]; + break; + case 2: + ndx = arguments[0]; + arg = arguments[1]; + break; + default: toss3("Invalid bind() arguments."); + } + if (void 0 === arg) return this; + else if (!this.parameterCount) toss3("This statement has no bindable parameters."); + __stmtMayGet.delete(this); + if (null === arg) return bindOne(this, ndx, BindTypes.null, arg); + else if (Array.isArray(arg)) { + if (1 !== arguments.length) toss3("When binding an array, an index argument is not permitted."); + arg.forEach((v, i) => bindOne(this, i + 1, affirmSupportedBindType(v), v)); + return this; + } else if (arg instanceof ArrayBuffer) arg = new Uint8Array(arg); + if ("object" === typeof arg && !util.isBindableTypedArray(arg)) { + if (1 !== arguments.length) toss3("When binding an object, an index argument is not permitted."); + Object.keys(arg).forEach((k) => bindOne(this, k, affirmSupportedBindType(arg[k]), arg[k])); + return this; + } else return bindOne(this, ndx, affirmSupportedBindType(arg), arg); + toss3("Should not reach this point."); + }, + bindAsBlob: function(ndx, arg) { + affirmStmtOpen(this); + if (1 === arguments.length) { + arg = ndx; + ndx = 1; + } + const t = affirmSupportedBindType(arg); + if (BindTypes.string !== t && BindTypes.blob !== t && BindTypes.null !== t) toss3("Invalid value type for bindAsBlob()"); + return bindOne(this, ndx, BindTypes.blob, arg); + }, + step: function() { + affirmNotLockedByExec(this, "step()"); + const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); + switch (rc) { + case capi.SQLITE_DONE: + __stmtMayGet.delete(this); + return false; + case capi.SQLITE_ROW: + __stmtMayGet.add(this); + return true; + default: + __stmtMayGet.delete(this); + sqlite3.config.warn("sqlite3_step() rc=", rc, capi.sqlite3_js_rc_str(rc), "SQL =", capi.sqlite3_sql(this.pointer)); + DB.checkRc(this.db.pointer, rc); + } + }, + stepReset: function() { + this.step(); + return this.reset(); + }, + stepFinalize: function() { + try { + const rc = this.step(); + this.reset(); + return rc; + } finally { + try { + this.finalize(); + } catch (e) {} + } + }, + get: function(ndx, asType) { + if (!__stmtMayGet.has(affirmStmtOpen(this))) toss3("Stmt.step() has not (recently) returned true."); + if (Array.isArray(ndx)) { + let i = 0; + const n = this.columnCount; + while (i < n) ndx[i] = this.get(i++); + return ndx; + } else if (ndx && "object" === typeof ndx) { + let i = 0; + const n = this.columnCount; + while (i < n) ndx[capi.sqlite3_column_name(this.pointer, i)] = this.get(i++); + return ndx; + } + affirmColIndex(this, ndx); + switch (void 0 === asType ? capi.sqlite3_column_type(this.pointer, ndx) : asType) { + case capi.SQLITE_NULL: return null; + case capi.SQLITE_INTEGER: if (wasm.bigIntEnabled) { + const rc = capi.sqlite3_column_int64(this.pointer, ndx); + if (rc >= Number.MIN_SAFE_INTEGER && rc <= Number.MAX_SAFE_INTEGER) return Number(rc).valueOf(); + return rc; + } else { + const rc = capi.sqlite3_column_double(this.pointer, ndx); + if (rc > Number.MAX_SAFE_INTEGER || rc < Number.MIN_SAFE_INTEGER) toss3("Integer is out of range for JS integer range: " + rc); + return util.isInt32(rc) ? rc | 0 : rc; + } + case capi.SQLITE_FLOAT: return capi.sqlite3_column_double(this.pointer, ndx); + case capi.SQLITE_TEXT: return capi.sqlite3_column_text(this.pointer, ndx); + case capi.SQLITE_BLOB: { + const n = capi.sqlite3_column_bytes(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx), rc = new Uint8Array(n); + if (n) { + rc.set(wasm.heap8u().slice(Number(ptr), Number(ptr) + n), 0); + if (this.db._blobXfer instanceof Array) this.db._blobXfer.push(rc.buffer); + } + return rc; + } + default: toss3("Don't know how to translate", "type of result column #" + ndx + "."); + } + toss3("Not reached."); + }, + getInt: function(ndx) { + return this.get(ndx, capi.SQLITE_INTEGER); + }, + getFloat: function(ndx) { + return this.get(ndx, capi.SQLITE_FLOAT); + }, + getString: function(ndx) { + return this.get(ndx, capi.SQLITE_TEXT); + }, + getBlob: function(ndx) { + return this.get(ndx, capi.SQLITE_BLOB); + }, + getJSON: function(ndx) { + const s = this.get(ndx, capi.SQLITE_STRING); + return null === s ? s : JSON.parse(s); + }, + getColumnName: function(ndx) { + return capi.sqlite3_column_name(affirmColIndex(affirmStmtOpen(this), ndx).pointer, ndx); + }, + getColumnNames: function(tgt = []) { + affirmColIndex(affirmStmtOpen(this), 0); + const n = this.columnCount; + for (let i = 0; i < n; ++i) tgt.push(capi.sqlite3_column_name(this.pointer, i)); + return tgt; + }, + getParamIndex: function(name) { + return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_index(this.pointer, name) : void 0; + }, + getParamName: function(ndx) { + return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_name(this.pointer, ndx) : void 0; + }, + isBusy: function() { + return 0 !== capi.sqlite3_stmt_busy(affirmStmtOpen(this)); + }, + isReadOnly: function() { + return 0 !== capi.sqlite3_stmt_readonly(affirmStmtOpen(this)); + } + }; + { + const prop = { + enumerable: true, + get: function() { + return __ptrMap.get(this); + }, + set: () => toss3("The pointer property is read-only.") + }; + Object.defineProperty(Stmt.prototype, "pointer", prop); + Object.defineProperty(DB.prototype, "pointer", prop); + } + /** + Stmt.columnCount is an interceptor for sqlite3_column_count(). + + This requires an unfortunate performance hit compared to caching + columnCount when the Stmt is created/prepared (as was done in + SQLite <=3.42.0), but is necessary in order to handle certain + corner cases, as described in + https://sqlite.org/forum/forumpost/7774b773937cbe0a. + */ + Object.defineProperty(Stmt.prototype, "columnCount", { + enumerable: false, + get: function() { + return capi.sqlite3_column_count(this.pointer); + }, + set: () => toss3("The columnCount property is read-only.") + }); + Object.defineProperty(Stmt.prototype, "parameterCount", { + enumerable: false, + get: function() { + return capi.sqlite3_bind_parameter_count(this.pointer); + }, + set: () => toss3("The parameterCount property is read-only.") + }); + /** + The Stmt counterpart of oo1.DB.wrapHandle(), this creates a Stmt + instance which wraps a WASM (sqlite3_stmt*) in the oo1 API, + optionally with or without taking over ownership of that pointer. + + The first argument must be an oo1.DB instance[^1]. + + The second argument must be a valid WASM (sqlite3_stmt*), as + produced by sqlite3_prepare_v2() and sqlite3_prepare_v3(). + + The third argument, defaulting to false, specifies whether the + returned Stmt object takes over ownership of the underlying + (sqlite3_stmt*). If true, the returned object's finalize() method + will finalize that handle, else it will not. If it is false, + ownership of pStmt is unchanged and pStmt MUST outlive the + returned object or results are undefined. + + This function throws if the arguments are invalid. On success it + returns a new Stmt object which wraps the given statement + pointer. + + Like all Stmt objects, the finalize() method must eventually be + called on the returned object to free up internal resources, + regardless of whether this function's third argument is true or + not. + + [^1]: The first argument cannot be a (sqlite3*) because the + resulting Stmt object requires a parent DB object. It is not yet + determined whether it would be of general benefit to refactor the + DB/Stmt pair internals to communicate in terms of the underlying + (sqlite3*) rather than a DB object. If so, we could laxen the + first argument's requirement and allow an (sqlite3*). Because + DB.wrapHandle() enables multiple DB objects to proxy the same + (sqlite3*), we cannot unambiguously translate the first arugment + from (sqlite3*) to DB instances for us with this function's first + argument. + */ + Stmt.wrapHandle = function(oo1db, pStmt, takeOwnership = false) { + if (!(oo1db instanceof DB) || !oo1db.pointer) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "First argument must be an opened sqlite3.oo1.DB instance"); + if (!pStmt || !wasm.isPtr(pStmt)) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "Second argument must be a WASM sqlite3_stmt pointer"); + return new Stmt(oo1db, pStmt, BindTypes, !!takeOwnership); + }; + /** The OO API's public namespace. */ + sqlite3.oo1 = { + DB, + Stmt + }; + }); + /** + 2022-07-22 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This file implements the initializer for SQLite's "Worker API #1", a + very basic DB access API intended to be scripted from a main window + thread via Worker-style messages. Because of limitations in that + type of communication, this API is minimalistic and only capable of + serving relatively basic DB requests (e.g. it cannot process nested + query loops concurrently). + + This file requires that the core C-style sqlite3 API and OO API #1 + have been loaded. + */ + /** + sqlite3.initWorker1API() implements a Worker-based wrapper around + SQLite3 OO API #1, colloquially known as "Worker API #1". + + In order to permit this API to be loaded in worker threads without + automatically registering onmessage handlers, initializing the + worker API requires calling initWorker1API(). If this function is + called from a non-worker thread then it throws an exception. It + must only be called once per Worker. + + When initialized, it installs message listeners to receive Worker + messages and then it posts a message in the form: + + ``` + {type:'sqlite3-api', result:'worker1-ready'} + ``` + + to let the client know that it has been initialized. Clients may + optionally depend on this function not returning until + initialization is complete, as the initialization is synchronous. + In some contexts, however, listening for the above message is + a better fit. + + Note that the worker-based interface can be slightly quirky because + of its async nature. In particular, any number of messages may be posted + to the worker before it starts handling any of them. If, e.g., an + "open" operation fails, any subsequent messages will fail. The + Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`) + is more comfortable to use in that regard. + + The documentation for the input and output worker messages for + this API follows... + + ==================================================================== + Common message format... + + Each message posted to the worker has an operation-independent + envelope and operation-dependent arguments: + + ``` + { + type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get' + + messageId: OPTIONAL arbitrary value. The worker will copy it as-is + into response messages to assist in client-side dispatching. + + dbId: a db identifier string (returned by 'open') which tells the + operation which database instance to work on. If not provided, the + first-opened db is used. This is an "opaque" value, with no + inherently useful syntax or information. Its value is subject to + change with any given build of this API and cannot be used as a + basis for anything useful beyond its one intended purpose. + + args: ...operation-dependent arguments... + + // the framework may add other properties for testing or debugging + // purposes. + + } + ``` + + Response messages, posted back to the main thread, look like: + + ``` + { + type: string. Same as above except for error responses, which have the type + 'error', + + messageId: same value, if any, provided by the inbound message + + dbId: the id of the db which was operated on, if any, as returned + by the corresponding 'open' operation. + + result: ...operation-dependent result... + + } + ``` + + ==================================================================== + Error responses + + Errors are reported messages in an operation-independent format: + + ``` + { + type: "error", + + messageId: ...as above..., + + dbId: ...as above... + + result: { + + operation: type of the triggering operation: 'open', 'close', ... + + message: ...error message text... + + errorClass: string. The ErrorClass.name property from the thrown exception. + + input: the message object which triggered the error. + + stack: _if available_, a stack trace array. + + } + + } + ``` + + + ==================================================================== + "config-get" + + This operation fetches the serializable parts of the sqlite3 API + configuration. + + Message format: + + ``` + { + type: "config-get", + messageId: ...as above..., + args: currently ignored and may be elided. + } + ``` + + Response: + + ``` + { + type: "config-get", + messageId: ...as above..., + result: { + + version: sqlite3.version object + + bigIntEnabled: bool. True if BigInt support is enabled. + + vfsList: result of sqlite3.capi.sqlite3_js_vfs_list() + } + } + ``` + + + ==================================================================== + "open" a database + + Message format: + + ``` + { + type: "open", + messageId: ...as above..., + args:{ + + filename [=":memory:" or "" (unspecified)]: the db filename. + See the sqlite3.oo1.DB constructor for peculiarities and + transformations, + + vfs: sqlite3_vfs name. Ignored if filename is ":memory:" or "". + This may change how the given filename is resolved. + } + } + ``` + + Response: + + ``` + { + type: "open", + messageId: ...as above..., + result: { + filename: db filename, possibly differing from the input. + + dbId: an opaque ID value which must be passed in the message + envelope to other calls in this API to tell them which db to + use. If it is not provided to future calls, they will default to + operating on the least-recently-opened db. This property is, for + API consistency's sake, also part of the containing message + envelope. Only the `open` operation includes it in the `result` + property. + + persistent: true if the given filename resides in the + known-persistent storage, else false. + + vfs: name of the VFS the "main" db is using. + } + } + ``` + + ==================================================================== + "close" a database + + Message format: + + ``` + { + type: "close", + messageId: ...as above... + dbId: ...as above... + args: OPTIONAL {unlink: boolean} + } + ``` + + If the `dbId` does not refer to an opened ID, this is a no-op. If + the `args` object contains a truthy `unlink` value then the database + will be unlinked (deleted) after closing it. The inability to close a + db (because it's not opened) or delete its file does not trigger an + error. + + Response: + + ``` + { + type: "close", + messageId: ...as above..., + result: { + + filename: filename of closed db, or undefined if no db was closed + + } + } + ``` + + ==================================================================== + "exec" SQL + + All SQL execution is processed through the exec operation. It offers + most of the features of the oo1.DB.exec() method, with a few limitations + imposed by the state having to cross thread boundaries. + + Message format: + + ``` + { + type: "exec", + messageId: ...as above... + dbId: ...as above... + args: string (SQL) or {... see below ...} + } + ``` + + Response: + + ``` + { + type: "exec", + messageId: ...as above..., + dbId: ...as above... + result: { + input arguments, possibly modified. See below. + } + } + ``` + + The arguments are in the same form accepted by oo1.DB.exec(), with + the exceptions noted below. + + If `args.countChanges` (added in version 3.43) is truthy then the + `result` property contained by the returned object will have a + `changeCount` property which holds the number of changes made by the + provided SQL. Because the SQL may contain an arbitrary number of + statements, the `changeCount` is calculated by calling + `sqlite3_total_changes()` before and after the SQL is evaluated. If + the value of `countChanges` is 64 then the `changeCount` property + will be returned as a 64-bit integer in the form of a BigInt (noting + that that will trigger an exception if used in a BigInt-incapable + build). In the latter case, the number of changes is calculated by + calling `sqlite3_total_changes64()` before and after the SQL is + evaluated. + + If the `args.lastInsertRowId` (added in version 3.50.0) is truthy + then the `result` property contained by the returned object will + have a `lastInsertRowId` will hold a BigInt-type value corresponding + to the result of sqlite3_last_insert_rowid(). This value is only + fetched once, after the SQL is run, regardless of how many + statements the SQL contains. This API has no idea whether the SQL + contains any INSERTs, so it is up to the client to apply/rely on + this property only when it makes sense to do so. + + A function-type args.callback property cannot cross + the window/Worker boundary, so is not useful here. If + args.callback is a string then it is assumed to be a + message type key, in which case a callback function will be + applied which posts each row result via: + + postMessage({type: thatKeyType, + rowNumber: 1-based-#, + row: theRow, + columnNames: anArray + }) + + And, at the end of the result set (whether or not any result rows + were produced), it will post an identical message with + (row=undefined, rowNumber=null) to alert the caller than the result + set is completed. Note that a row value of `null` is a legal row + result for certain arg.rowMode values. + + (Design note: we don't use (row=undefined, rowNumber=undefined) to + indicate end-of-results because fetching those would be + indistinguishable from fetching from an empty object unless the + client used hasOwnProperty() (or similar) to distinguish "missing + property" from "property with the undefined value". Similarly, + `null` is a legal value for `row` in some case , whereas the db + layer won't emit a result value of `undefined`.) + + The callback proxy must not recurse into this interface. An exec() + call will tie up the Worker thread, causing any recursion attempt + to wait until the first exec() is completed. + + The response is the input options object (or a synthesized one if + passed only a string), noting that options.resultRows and + options.columnNames may be populated by the call to db.exec(). + + + ==================================================================== + "export" the current db + + To export the underlying database as a byte array... + + Message format: + + ``` + { + type: "export", + messageId: ...as above..., + dbId: ...as above... + } + ``` + + Response: + + ``` + { + type: "export", + messageId: ...as above..., + dbId: ...as above... + result: { + byteArray: Uint8Array (as per sqlite3_js_db_export()), + filename: the db filename, + mimetype: "application/x-sqlite3" + } + } + ``` + + */ + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + const util = sqlite3.util; + sqlite3.initWorker1API = function() { + "use strict"; + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + if (!(globalThis.WorkerGlobalScope instanceof Function)) toss("initWorker1API() must be run from a Worker thread."); + const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); + const DB = sqlite3.oo1.DB; + /** + Returns the app-wide unique ID for the given db, creating one if + needed. + */ + const getDbId = function(db) { + let id = wState.idMap.get(db); + if (id) return id; + id = "db#" + ++wState.idSeq + ":" + Math.floor(Math.random() * 1e8) + ":" + Math.floor(Math.random() * 1e8); + /** ^^^ can't simply use db.pointer b/c closing/opening may re-use + the same address, which could map pending messages to a wrong + instance. + + 2025-07: https://github.com/sqlite/sqlite-wasm/issues/113 + demonstrates that two Worker1s can end up with the same IDs, + despite using different instances of the library, so we need + to add some randomness to the IDs instead of relying on the + pointer addresses. + */ + wState.idMap.set(db, id); + return id; + }; + /** + Internal helper for managing Worker-level state. + */ + const wState = { + dbList: [], + idSeq: 0, + idMap: /* @__PURE__ */ new WeakMap(), + xfer: [], + open: function(opt) { + const db = new DB(opt); + this.dbs[getDbId(db)] = db; + if (this.dbList.indexOf(db) < 0) this.dbList.push(db); + return db; + }, + close: function(db, alsoUnlink) { + if (db) { + delete this.dbs[getDbId(db)]; + const filename = db.filename; + const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0); + db.close(); + const ddNdx = this.dbList.indexOf(db); + if (ddNdx >= 0) this.dbList.splice(ddNdx, 1); + if (alsoUnlink && filename && pVfs) util.sqlite3__wasm_vfs_unlink(pVfs, filename); + } + }, + post: function(msg, xferList) { + if (xferList && xferList.length) { + globalThis.postMessage(msg, Array.from(xferList)); + xferList.length = 0; + } else globalThis.postMessage(msg); + }, + dbs: Object.create(null), + getDb: function(id, require = true) { + return this.dbs[id] || (require ? toss("Unknown (or closed) DB ID:", id) : void 0); + } + }; + /** Throws if the given db is falsy or not opened, else returns its + argument. */ + const affirmDbOpen = function(db = wState.dbList[0]) { + return db && db.pointer ? db : toss("DB is not opened."); + }; + /** Extract dbId from the given message payload. */ + const getMsgDb = function(msgData, affirmExists = true) { + const db = wState.getDb(msgData.dbId, false) || wState.dbList[0]; + return affirmExists ? affirmDbOpen(db) : db; + }; + const getDefaultDbId = function() { + return wState.dbList[0] && getDbId(wState.dbList[0]); + }; + /** + A level of "organizational abstraction" for the Worker1 + API. Each method in this object must map directly to a Worker1 + message type key. The onmessage() dispatcher attempts to + dispatch all inbound messages to a method of this object, + passing it the event.data part of the inbound event object. All + methods must return a plain Object containing any result + state, which the dispatcher may amend. All methods must throw + on error. + */ + const wMsgHandler = { + open: function(ev) { + const oargs = Object.create(null), args = ev.args || Object.create(null); + if (args.simulateError) toss("Throwing because of simulateError flag."); + const rc = Object.create(null); + oargs.vfs = args.vfs; + oargs.filename = args.filename || ""; + const db = wState.open(oargs); + rc.filename = db.filename; + rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); + rc.dbId = getDbId(db); + rc.vfs = db.dbVfsName(); + return rc; + }, + close: function(ev) { + const db = getMsgDb(ev, false); + const response = { filename: db && db.filename }; + if (db) { + const doUnlink = ev.args && "object" === typeof ev.args ? !!ev.args.unlink : false; + wState.close(db, doUnlink); + } + return response; + }, + exec: function(ev) { + const rc = "string" === typeof ev.args ? { sql: ev.args } : ev.args || Object.create(null); + if ("stmt" === rc.rowMode) toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); + else if (!rc.sql) toss("'exec' requires input SQL."); + const db = getMsgDb(ev); + if (rc.callback || Array.isArray(rc.resultRows)) db._blobXfer = wState.xfer; + const theCallback = rc.callback; + let rowNumber = 0; + const hadColNames = !!rc.columnNames; + if ("string" === typeof theCallback) { + if (!hadColNames) rc.columnNames = []; + rc.callback = function(row, stmt) { + wState.post({ + type: theCallback, + columnNames: rc.columnNames, + rowNumber: ++rowNumber, + row + }, wState.xfer); + }; + } + try { + const changeCount = !!rc.countChanges ? db.changes(true, 64 === rc.countChanges) : void 0; + db.exec(rc); + if (void 0 !== changeCount) rc.changeCount = db.changes(true, 64 === rc.countChanges) - changeCount; + const lastInsertRowId = !!rc.lastInsertRowId ? sqlite3.capi.sqlite3_last_insert_rowid(db) : void 0; + if (void 0 !== lastInsertRowId) rc.lastInsertRowId = lastInsertRowId; + if (rc.callback instanceof Function) { + rc.callback = theCallback; + wState.post({ + type: theCallback, + columnNames: rc.columnNames, + rowNumber: null, + row: void 0 + }); + } + } finally { + delete db._blobXfer; + if (rc.callback) rc.callback = theCallback; + } + return rc; + }, + "config-get": function() { + const rc = Object.create(null), src = sqlite3.config; + ["bigIntEnabled"].forEach(function(k) { + if (Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; + }); + rc.version = sqlite3.version; + rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list(); + return rc; + }, + export: function(ev) { + const db = getMsgDb(ev); + const response = { + byteArray: sqlite3.capi.sqlite3_js_db_export(db.pointer), + filename: db.filename, + mimetype: "application/x-sqlite3" + }; + wState.xfer.push(response.byteArray.buffer); + return response; + }, + toss: function(ev) { + toss("Testing worker exception"); + } + }; + globalThis.onmessage = async function(ev) { + ev = ev.data; + let result, dbId = ev.dbId, evType = ev.type; + const arrivalTime = performance.now(); + try { + if (wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function) result = await wMsgHandler[evType](ev); + else toss("Unknown db worker message type:", ev.type); + } catch (err) { + evType = "error"; + result = { + operation: ev.type, + message: err.message, + errorClass: err.name, + input: ev + }; + if (err.stack) result.stack = "string" === typeof err.stack ? err.stack.split(/\n\s*/) : err.stack; + } + if (!dbId) dbId = result.dbId || getDefaultDbId(); + wState.post({ + type: evType, + dbId, + messageId: ev.messageId, + workerReceivedTime: arrivalTime, + workerRespondTime: performance.now(), + departureTime: ev.departureTime, + result + }, wState.xfer); + }; + globalThis.postMessage({ + type: "sqlite3-api", + result: "worker1-ready" + }); + }.bind({ sqlite3 }); + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; + const vfs = Object.create(null); + sqlite3.vfs = vfs; + /** + Uses sqlite3_vfs_register() to register this + sqlite3.capi.sqlite3_vfs instance. This object must have already + been filled out properly. If the first argument is truthy, the + VFS is registered as the default VFS, else it is not. + + On success, returns this object. Throws on error. + */ + capi.sqlite3_vfs.prototype.registerVfs = function(asDefault = false) { + if (!(this instanceof sqlite3.capi.sqlite3_vfs)) toss("Expecting a sqlite3_vfs-type argument."); + const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0); + if (rc) toss("sqlite3_vfs_register(", this, ") failed with rc", rc); + if (this.pointer !== capi.sqlite3_vfs_find(this.$zName)) toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", this); + return this; + }; + /** + A wrapper for + sqlite3.StructBinder.StructType.prototype.installMethods() or + registerVfs() to reduce installation of a VFS and/or its I/O + methods to a single call. + + Accepts an object which contains the properties "io" and/or + "vfs", each of which is itself an object with following properties: + + - `struct`: an sqlite3.StructBinder.StructType-type struct. This + must be a populated (except for the methods) object of type + sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the + "vfs" entry). + + - `methods`: an object mapping sqlite3_io_methods method names + (e.g. 'xClose') to JS implementations of those methods. The JS + implementations must be call-compatible with their native + counterparts. + + For each of those object, this function passes its (`struct`, + `methods`, (optional) `applyArgcCheck`) properties to + installMethods(). + + If the `vfs` entry is set then: + + - Its `struct` property's registerVfs() is called. The + `vfs` entry may optionally have an `asDefault` property, which + gets passed as the argument to registerVfs(). + + - If `struct.$zName` is falsy and the entry has a string-type + `name` property, `struct.$zName` is set to the C-string form of + that `name` value before registerVfs() is called. That string + gets added to the on-dispose state of the struct. + + On success returns this object. Throws on error. + */ + vfs.installVfs = function(opt) { + let count = 0; + const propList = ["io", "vfs"]; + for (const key of propList) { + const o = opt[key]; + if (o) { + ++count; + o.struct.installMethods(o.methods, !!o.applyArgcCheck); + if ("vfs" === key) { + if (!o.struct.$zName && "string" === typeof o.name) o.struct.addOnDispose(o.struct.$zName = wasm.allocCString(o.name)); + o.struct.registerVfs(!!o.asDefault); + } + } + } + if (!count) toss("Misuse: installVfs() options object requires at least", "one of:", propList); + return this; + }; + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + if (!sqlite3.wasm.exports.sqlite3_declare_vtab) return; + const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; + const vtab = Object.create(null); + sqlite3.vtab = vtab; + const sii = capi.sqlite3_index_info; + /** + If n is >=0 and less than this.$nConstraint, this function + returns either a WASM pointer to the 0-based nth entry of + this.$aConstraint (if passed a truthy 2nd argument) or an + sqlite3_index_info.sqlite3_index_constraint object wrapping that + address (if passed a falsy value or no 2nd argument). Returns a + falsy value if n is out of range. + */ + sii.prototype.nthConstraint = function(n, asPtr = false) { + if (n < 0 || n >= this.$nConstraint) return false; + const ptr = wasm.ptr.add(this.$aConstraint, sii.sqlite3_index_constraint.structInfo.sizeof * n); + return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr); + }; + /** + Works identically to nthConstraint() but returns state from + this.$aConstraintUsage, so returns an + sqlite3_index_info.sqlite3_index_constraint_usage instance + if passed no 2nd argument or a falsy 2nd argument. + */ + sii.prototype.nthConstraintUsage = function(n, asPtr = false) { + if (n < 0 || n >= this.$nConstraint) return false; + const ptr = wasm.ptr.add(this.$aConstraintUsage, sii.sqlite3_index_constraint_usage.structInfo.sizeof * n); + return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr); + }; + /** + If n is >=0 and less than this.$nOrderBy, this function + returns either a WASM pointer to the 0-based nth entry of + this.$aOrderBy (if passed a truthy 2nd argument) or an + sqlite3_index_info.sqlite3_index_orderby object wrapping that + address (if passed a falsy value or no 2nd argument). Returns a + falsy value if n is out of range. + */ + sii.prototype.nthOrderBy = function(n, asPtr = false) { + if (n < 0 || n >= this.$nOrderBy) return false; + const ptr = wasm.ptr.add(this.$aOrderBy, sii.sqlite3_index_orderby.structInfo.sizeof * n); + return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr); + }; + /** + Internal factory function for xVtab and xCursor impls. + */ + const __xWrapFactory = function(methodName, StructType) { + return function(ptr, removeMapping = false) { + if (0 === arguments.length) ptr = new StructType(); + if (ptr instanceof StructType) { + this.set(ptr.pointer, ptr); + return ptr; + } else if (!wasm.isPtr(ptr)) sqlite3.SQLite3Error.toss("Invalid argument to", methodName + "()"); + let rc = this.get(ptr); + if (removeMapping) this.delete(ptr); + return rc; + }.bind(/* @__PURE__ */ new Map()); + }; + /** + A factory function which implements a simple lifetime manager for + mappings between C struct pointers and their JS-level wrappers. + The first argument must be the logical name of the manager + (e.g. 'xVtab' or 'xCursor'), which is only used for error + reporting. The second must be the capi.XYZ struct-type value, + e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor. + + Returns an object with 4 methods: create(), get(), unget(), and + dispose(), plus a StructType member with the value of the 2nd + argument. The methods are documented in the body of this + function. + */ + const StructPtrMapper = function(name, StructType) { + const __xWrap = __xWrapFactory(name, StructType); + /** + This object houses a small API for managing mappings of (`T*`) + to StructType objects, specifically within the lifetime + requirements of sqlite3_module methods. + */ + return Object.assign(Object.create(null), { + StructType, + create: (ppOut) => { + const rc = __xWrap(); + wasm.pokePtr(ppOut, rc.pointer); + return rc; + }, + get: (pCObj) => __xWrap(pCObj), + unget: (pCObj) => __xWrap(pCObj, true), + dispose: (pCObj) => __xWrap(pCObj, true)?.dispose?.() + }); + }; + /** + A lifetime-management object for mapping `sqlite3_vtab*` + instances in sqlite3_module methods to capi.sqlite3_vtab + objects. + + The API docs are in the API-internal StructPtrMapper(). + */ + vtab.xVtab = StructPtrMapper("xVtab", capi.sqlite3_vtab); + /** + A lifetime-management object for mapping `sqlite3_vtab_cursor*` + instances in sqlite3_module methods to capi.sqlite3_vtab_cursor + objects. + + The API docs are in the API-internal StructPtrMapper(). + */ + vtab.xCursor = StructPtrMapper("xCursor", capi.sqlite3_vtab_cursor); + /** + Convenience form of creating an sqlite3_index_info wrapper, + intended for use in xBestIndex implementations. Note that the + caller is expected to call dispose() on the returned object + before returning. Though not _strictly_ required, as that object + does not own the pIdxInfo memory, it is nonetheless good form. + */ + vtab.xIndexInfo = (pIdxInfo) => new capi.sqlite3_index_info(pIdxInfo); + /** + Given an sqlite3_module method name and error object, this + function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof + sqlite3.WasmAllocError), else it returns its second argument. Its + intended usage is in the methods of a sqlite3_vfs or + sqlite3_module: + + ``` + try{ + let rc = ... + return rc; + }catch(e){ + return sqlite3.vtab.xError( + 'xColumn', e, sqlite3.capi.SQLITE_XYZ); + // where SQLITE_XYZ is some call-appropriate result code. + } + ``` + + If no 3rd argument is provided, its default depends on + the error type: + + - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM. + + - If err is an SQLite3Error then its `resultCode` property + is used. + + - If all else fails, capi.SQLITE_ERROR is used. + + If xError.errorReporter is a function, it is called in + order to report the error, else the error is not reported. + If that function throws, that exception is ignored. + */ + vtab.xError = function f(methodName, err, defaultRc) { + if (f.errorReporter instanceof Function) try { + f.errorReporter("sqlite3_module::" + methodName + "(): " + err.message); + } catch (e) {} + let rc; + if (err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM; + else if (arguments.length > 2) rc = defaultRc; + else if (err instanceof sqlite3.SQLite3Error) rc = err.resultCode; + return rc || capi.SQLITE_ERROR; + }; + vtab.xError.errorReporter = sqlite3.config.error.bind(sqlite3.config); + /** + A helper for sqlite3_vtab::xRowid() and xUpdate() + implementations. It must be passed the final argument to one of + those methods (an output pointer to an int64 row ID) and the + value to store at the output pointer's address. Returns the same + as wasm.poke() and will throw if the 1st or 2nd arguments + are invalid for that function. + + Example xRowid impl: + + ``` + const xRowid = (pCursor, ppRowid64)=>{ + const c = vtab.xCursor(pCursor); + vtab.xRowid(ppRowid64, c.myRowId); + return 0; + }; + ``` + */ + vtab.xRowid = (ppRowid64, value) => wasm.poke(ppRowid64, value, "i64"); + /** + A helper to initialize and set up an sqlite3_module object for + later installation into individual databases using + sqlite3_create_module(). Requires an object with the following + properties: + + - `methods`: an object containing a mapping of properties with + the C-side names of the sqlite3_module methods, e.g. xCreate, + xBestIndex, etc., to JS implementations for those functions. + Certain special-case handling is performed, as described below. + + - `catchExceptions` (default=false): if truthy, the given methods + are not mapped as-is, but are instead wrapped inside wrappers + which translate exceptions into result codes of SQLITE_ERROR or + SQLITE_NOMEM, depending on whether the exception is an + sqlite3.WasmAllocError. In the case of the xConnect and xCreate + methods, the exception handler also sets the output error + string to the exception's error string. + + - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If + not set, one will be created automatically. If the current + "this" is-a sqlite3_module then it is unconditionally used in + place of `struct`. + + - OPTIONAL `iVersion`: if set, it must be an integer value and it + gets assigned to the `$iVersion` member of the struct object. + If it's _not_ set, and the passed-in `struct` object's `$iVersion` + is 0 (the default) then this function attempts to define a value + for that property based on the list of methods it has. + + If `catchExceptions` is false, it is up to the client to ensure + that no exceptions escape the methods, as doing so would move + them through the C API, leading to undefined + behavior. (vtab.xError() is intended to assist in reporting + such exceptions.) + + Certain methods may refer to the same implementation. To simplify + the definition of such methods: + + - If `methods.xConnect` is `true` then the value of + `methods.xCreate` is used in its place, and vice versa. sqlite + treats xConnect/xCreate functions specially if they are exactly + the same function (same pointer value). + + - If `methods.xDisconnect` is true then the value of + `methods.xDestroy` is used in its place, and vice versa. + + This is to facilitate creation of those methods inline in the + passed-in object without requiring the client to explicitly get a + reference to one of them in order to assign it to the other + one. + + The `catchExceptions`-installed handlers will account for + identical references to the above functions and will install the + same wrapper function for both. + + The given methods are expected to return integer values, as + expected by the C API. If `catchExceptions` is truthy, the return + value of the wrapped function will be used as-is and will be + translated to 0 if the function returns a falsy value (e.g. if it + does not have an explicit return). If `catchExceptions` is _not_ + active, the method implementations must explicitly return integer + values. + + Throws on error. On success, returns the sqlite3_module object + (`this` or `opt.struct` or a new sqlite3_module instance, + depending on how it's called). + */ + vtab.setupModule = function(opt) { + let createdMod = false; + const mod = this instanceof capi.sqlite3_module ? this : opt.struct || (createdMod = new capi.sqlite3_module()); + try { + const methods = opt.methods || toss("Missing 'methods' object."); + for (const e of Object.entries({ + xConnect: "xCreate", + xDisconnect: "xDestroy" + })) { + const k = e[0], v = e[1]; + if (true === methods[k]) methods[k] = methods[v]; + else if (true === methods[v]) methods[v] = methods[k]; + } + if (opt.catchExceptions) { + const fwrap = function(methodName, func) { + if (["xConnect", "xCreate"].indexOf(methodName) >= 0) return function(pDb, pAux, argc, argv, ppVtab, pzErr) { + try { + return func(...arguments) || 0; + } catch (e) { + if (!(e instanceof sqlite3.WasmAllocError)) { + wasm.dealloc(wasm.peekPtr(pzErr)); + wasm.pokePtr(pzErr, wasm.allocCString(e.message)); + } + return vtab.xError(methodName, e); + } + }; + else return function(...args) { + try { + return func(...args) || 0; + } catch (e) { + return vtab.xError(methodName, e); + } + }; + }; + const mnames = [ + "xCreate", + "xConnect", + "xBestIndex", + "xDisconnect", + "xDestroy", + "xOpen", + "xClose", + "xFilter", + "xNext", + "xEof", + "xColumn", + "xRowid", + "xUpdate", + "xBegin", + "xSync", + "xCommit", + "xRollback", + "xFindFunction", + "xRename", + "xSavepoint", + "xRelease", + "xRollbackTo", + "xShadowName" + ]; + const remethods = Object.create(null); + for (const k of mnames) { + const m = methods[k]; + if (!(m instanceof Function)) continue; + else if ("xConnect" === k && methods.xCreate === m) remethods[k] = methods.xCreate; + else if ("xCreate" === k && methods.xConnect === m) remethods[k] = methods.xConnect; + else remethods[k] = fwrap(k, m); + } + mod.installMethods(remethods, false); + } else mod.installMethods(methods, !!opt.applyArgcCheck); + if (0 === mod.$iVersion) { + let v; + if ("number" === typeof opt.iVersion) v = opt.iVersion; + else if (mod.$xIntegrity) v = 4; + else if (mod.$xShadowName) v = 3; + else if (mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2; + else v = 1; + mod.$iVersion = v; + } + } catch (e) { + if (createdMod) createdMod.dispose(); + throw e; + } + return mod; + }; + /** + Equivalent to calling vtab.setupModule() with this sqlite3_module + object as the call's `this`. + */ + capi.sqlite3_module.prototype.setupModule = function(opt) { + return vtab.setupModule.call(this, opt); + }; + }); + /** + kvvfs - the Key/Value VFS - is an SQLite3 VFS which delegates + storage of its pages and metadata to a key-value store. + + It was conceived in order to support JS's localStorage and + sessionStorage objects. Its native implementation uses files as + key/value storage (one file per record) but the JS implementation + replaces a few methods so that it can use the aforementioned + objects as storage. + + It uses a bespoke ASCII encoding to store each db page as a + separate record and stores some metadata, like the db's unencoded + size and its journal, as individual records. + + kvvfs is significantly less efficient than a plain in-memory db but + it also, as a side effect of its design, offers a JSON-friendly + interchange format for exporting and importing databases. + + kvvfs is _not_ designed for heavy db loads. It is relatively + malloc()-heavy, having to de/allocate frequently, and it + spends much of its time converting the raw db pages into and out of + an ASCII encoding. + + But it _does_ work and is "performant enough" for db work of the + scale of a db which will fit within sessionStorage or localStorage + (just 2-3mb). + + "Version 2" extends it to support using Storage-like objects as + backing storage, Storage being the JS class which localStorage and + sessionStorage both derive from. This essentially moves the backing + store from whatever localStorage and sessionStorage use to an + in-memory object. + + This effort is primarily a stepping stone towards eliminating, if + it proves possible, the POSIX I/O API dependencies in SQLite's WASM + builds. That is: if this VFS works properly, it can be set as the + default VFS and we can eliminate the "unix" VFS from the JS/WASM + builds (as opposed to server-wise/WASI builds). That still, as of + 2025-11-23, a ways away, but it's the main driver for version 2 of + kvvfs. + + Version 2 remains compatible with version 1 databases and always + writes localStorage/sessionStorage metadata in the v1 format, so + such dbs can be manipulated freely by either version. For transient + storage objects (new in version 2), the format of its record keys + is simpified, requiring less space than v1 keys by eliding + redundant (in this context) info from the keys. + + Another benefit of v2 is its ability to export dbs into a + JSON-friendly (but not human-friendly) format. + + A potential, as-yet-unproven, benefit, would be the ability to plug + arbitrary Storage-compatible objects in so that clients could, + e.g. asynchronously post updates to db pages to some back-end for + backups. + */ + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + "use strict"; + const capi = sqlite3.capi, sqlite3_kvvfs_methods = capi.sqlite3_kvvfs_methods, KVVfsFile = capi.KVVfsFile, pKvvfs = sqlite3.capi.sqlite3_vfs_find("kvvfs"); + delete capi.sqlite3_kvvfs_methods; + delete capi.KVVfsFile; + if (!pKvvfs) return; + const util = sqlite3.util, wasm = sqlite3.wasm, toss3 = util.toss3, hop = (o, k) => Object.prototype.hasOwnProperty.call(o, k); + const kvvfsMethods = new sqlite3_kvvfs_methods(wasm.exports.sqlite3__wasm_kvvfs_methods()); + util.assert(32 <= kvvfsMethods.$nKeySize, "unexpected kvvfsMethods.$nKeySize: " + kvvfsMethods.$nKeySize); + /** + Most of the VFS-internal state. + */ + const cache = Object.assign(Object.create(null), { + rxJournalSuffix: /-journal$/, + zKeyJrnl: wasm.allocCString("jrnl"), + zKeySz: wasm.allocCString("sz"), + keySize: kvvfsMethods.$nKeySize, + buffer: Object.assign(Object.create(null), { + n: kvvfsMethods.$nBufferSize, + pool: Object.create(null) + }) + }); + /** + Returns a (cached) wasm.alloc()'d buffer of cache.buffer.n size, + throwing on OOM. + + We leak this one-time alloc because we've no better option. + sqlite3_vfs does not have a finalizer, so we've no place to hook + in the cleanup. We "could" extend sqlite3_shutdown() to have a + cleanup list for stuff like this but that function is never + used in JS, so it's hardly worth it. + */ + cache.memBuffer = (id = 0) => cache.buffer.pool[id] ??= wasm.alloc(cache.buffer.n); + /** Frees the buffer with the given id. */ + cache.memBufferFree = (id) => { + const b = cache.buffer.pool[id]; + if (b) { + wasm.dealloc(b); + delete cache.buffer.pool[id]; + } + }; + const noop = () => {}; + const debug = sqlite3.__isUnderTest ? (...args) => sqlite3.config.debug("kvvfs:", ...args) : noop; + const warn = (...args) => sqlite3.config.warn("kvvfs:", ...args); + const error = (...args) => sqlite3.config.error("kvvfs:", ...args); + /** + Implementation of JS's Storage interface for use as backing store + of the kvvfs. Storage is a native class and its constructor + cannot be legally called from JS, making it impossible to + directly subclass Storage. This class implements (only) the + Storage interface, to make it a drop-in replacement for + localStorage/sessionStorage. (Any behavioral discrepancies are to + be considered bugs.) + + This impl simply proxies a plain, prototype-less Object, suitable + for JSON-ing. + + Design note: Storage has a bit of an odd iteration-related + interface as does not (AFAIK) specify specific behavior regarding + modification during traversal. Because of that, this class does + some seemingly unnecessary things with its #keys member, deleting + and recreating it whenever a property index might be invalidated. + */ + class KVVfsStorage { + #map; + #keys; + #getKeys() { + return this.#keys ??= Object.keys(this.#map); + } + constructor() { + this.clear(); + } + key(n) { + const k = this.#getKeys(); + return n < k.length ? k[n] : null; + } + getItem(k) { + return this.#map[k] ?? null; + } + setItem(k, v) { + if (!hop(this.#map, k)) this.#keys = null; + this.#map[k] = "" + v; + } + removeItem(k) { + if (delete this.#map[k]) this.#keys = null; + } + clear() { + this.#map = Object.create(null); + this.#keys = null; + } + get length() { + return this.#getKeys().length; + } + } + /** True if v is the name of one of the special persistant Storage + objects. */ + const kvvfsIsPersistentName = (v) => "local" === v || "session" === v; + /** + Keys in kvvfs have a prefix of "kvvfs-NAME-", where NAME is the + db name. This key is redundant in JS but it's how kvvfs works (it + saves each key to a separate file, so needs a distinct namespace + per data source name). We retain this prefix in 'local' and + 'session' storage for backwards compatibility and so that they + can co-exist with client data in their storage, but we elide them + from "v2" storage, where they're superfluous. + */ + const kvvfsKeyPrefix = (v) => kvvfsIsPersistentName(v) ? "kvvfs-" + v + "-" : ""; + /** + Throws if storage name n (JS string) is not valid for use as a + storage name. Much of this goes back to kvvfs having a fixed + buffer size for its keys, and the storage name needing to be + encoded in the keys for local/session storage. + + The second argument must only be true when called from xOpen() - + it makes names with a "-journal" suffix legal. + */ + const validateStorageName = function(n, mayBeJournal = false) { + if (kvvfsIsPersistentName(n)) return; + const len = new Blob([n]).size; + if (!len) toss3(capi.SQLITE_MISUSE, "Empty name is not permitted."); + let maxLen = cache.keySize - 1; + if (cache.rxJournalSuffix.test(n)) { + if (!mayBeJournal) toss3(capi.SQLITE_MISUSE, "Storage names may not have a '-journal' suffix."); + } else if (["-wal", "-shm"].filter((v) => n.endsWith(v)).length) toss3(capi.SQLITE_MISUSE, "Storage names may not have a -wal or -shm suffix."); + else maxLen -= 8; + if (len > maxLen) toss3(capi.SQLITE_RANGE, "Storage name is too long. Limit =", maxLen); + let i; + for (i = 0; i < len; ++i) { + const ch = n.codePointAt(i); + if (ch < 32) toss3(capi.SQLITE_RANGE, "Illegal character (" + ch + "d) in storage name:", n); + } + }; + /** + Create a new instance of the objects which go into + cache.storagePool, with a refcount of 1. If passed a Storage-like + object as its second argument, it is used for the storage, + otherwise it creates a new KVVfsStorage object. + */ + const newStorageObj = (name, storage = void 0) => Object.assign(Object.create(null), { + jzClass: name, + refc: 1, + deleteAtRefc0: false, + storage: storage || new KVVfsStorage(), + keyPrefix: kvvfsKeyPrefix(name), + files: [], + listeners: void 0 + }); + /** + Public interface for kvvfs v2. The capi.sqlite3_js_kvvfs_...() + routines remain in place for v1. Some members of this class proxy + to those functions but use different default argument values in + some cases. + */ + const kvvfs = sqlite3.kvvfs = Object.create(null); + if (sqlite3.__isUnderTest) kvvfs.log = Object.assign(Object.create(null), { + xOpen: false, + xClose: false, + xWrite: false, + xRead: false, + xSync: false, + xAccess: false, + xFileControl: false, + xRcrdRead: false, + xRcrdWrite: false, + xRcrdDelete: false + }); + /** + Deletes the cache.storagePool entries for store (a + cache.storagePool entry) and its db/journal counterpart. + */ + const deleteStorage = function(store) { + const other = cache.rxJournalSuffix.test(store.jzClass) ? store.jzClass.replace(cache.rxJournalSuffix, "") : store.jzClass + "-journal"; + kvvfs?.log?.xClose && debug("cleaning up storage handles [", store.jzClass, other, "]", store); + delete cache.storagePool[store.jzClass]; + delete cache.storagePool[other]; + if (!sqlite3.__isUnderTest) { + delete store.storage; + delete store.refc; + } + }; + /** + Add both store.jzClass and store.jzClass+"-journal" + to cache,storagePool. + */ + const installStorageAndJournal = (store) => cache.storagePool[store.jzClass] = cache.storagePool[store.jzClass + "-journal"] = store; + /** + The public name of the current thread's transient storage + object. A storage object with this name gets preinstalled. + */ + const nameOfThisThreadStorage = "."; + /** + Map of JS-stringified KVVfsFile::zClass names to + reference-counted Storage objects. These objects are created in + xOpen(). Their refcount is decremented in xClose(), and the + record is destroyed if the refcount reaches 0. We refcount so + that concurrent active xOpen()s on a given name, and within a + given thread, use the same storage object. + */ + cache.storagePool = Object.assign(Object.create(null), { [nameOfThisThreadStorage]: newStorageObj(nameOfThisThreadStorage) }); + if (globalThis.Storage) { + if (globalThis.localStorage instanceof globalThis.Storage) cache.storagePool.local = newStorageObj("local", globalThis.localStorage); + if (globalThis.sessionStorage instanceof globalThis.Storage) cache.storagePool.session = newStorageObj("session", globalThis.sessionStorage); + } + cache.builtinStorageNames = Object.keys(cache.storagePool); + const isBuiltinName = (n) => cache.builtinStorageNames.indexOf(n) > -1; + for (const k of Object.keys(cache.storagePool)) { + const orig = cache.storagePool[k]; + cache.storagePool[k + "-journal"] = orig; + } + cache.setError = (e = void 0, dfltErrCode = capi.SQLITE_ERROR) => { + if (e) { + cache.lastError = e; + return e.resultCode | 0 || dfltErrCode; + } + delete cache.lastError; + return 0; + }; + cache.popError = () => { + const e = cache.lastError; + delete cache.lastError; + return e; + }; + /** Exception handler for notifyListeners(). */ + const catchForNotify = (e) => { + warn("kvvfs.listener handler threw:", e); + }; + const kvvfsDecode = wasm.exports.sqlite3__wasm_kvvfs_decode; + const kvvfsEncode = wasm.exports.sqlite3__wasm_kvvfs_encode; + /** + Listener events and their argument(s) (via the callback(ev) + ev.data member): + + 'open': number of opened handles on this storage. + + 'close': number of opened handles on this storage. + + 'write': key, value + + 'delete': key + + 'sync': true if it's from xSync(), false if it's from + xFileControl(). + + For efficiency's sake, all calls to this function should + be in the form: + + store.listeners && notifyListeners(...); + + Failing to do so will trigger an exceptin in this function (which + will be ignored but may produce a console warning). + */ + const notifyListeners = async function(eventName, store, ...args) { + try { + if (store.keyPrefix && args[0]) args[0] = args[0].replace(store.keyPrefix, ""); + let u8enc, z0, z1, wcache; + for (const ear of store.listeners) { + const ev = Object.create(null); + ev.storageName = store.jzClass; + ev.type = eventName; + ear.decodePages; + const f = ear.events[eventName]; + if (f) { + if (!ear.includeJournal && args[0] === "jrnl") continue; + if ("write" === eventName && ear.decodePages && +args[0] > 0) { + ev.data = [args[0]]; + if (wcache?.[args[0]]) { + ev.data[1] = wcache[args[0]]; + continue; + } + u8enc ??= new TextEncoder("utf-8"); + z0 ??= cache.memBuffer(10); + z1 ??= cache.memBuffer(11); + const u = u8enc.encode(args[1]); + const heap = wasm.heap8u(); + heap.set(u, Number(z0)); + heap[wasm.ptr.addn(z0, u.length)] = 0; + const rc = kvvfsDecode(z0, z1, cache.buffer.n); + if (rc > 0) { + wcache ??= Object.create(null); + wcache[args[0]] = ev.data[1] = heap.slice(Number(z1), wasm.ptr.addn(z1, rc)); + } else continue; + } else ev.data = args.length ? args.length === 1 ? args[0] : args : void 0; + try { + f(ev)?.catch?.(catchForNotify); + } catch (e) { + warn("notifyListeners [", store.jzClass, "]", eventName, e); + } + } + } + } catch (e) { + catchForNotify(e); + } + }; + /** + Returns the storage object mapped to the given string zClass + (C-string pointer or JS string). + */ + const storageForZClass = (zClass) => "string" === typeof zClass ? cache.storagePool[zClass] : cache.storagePool[wasm.cstrToJs(zClass)]; + const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKey; + /** + Returns a C string from kvvfsMakeKey() OR returns zKey. In the + former case the memory is static, so must be copied before a + second call. zKey MUST be a pointer passed to a VFS/file method, + to allow us to avoid an alloc and/or an snprintf(). It requires + C-string arguments for zClass and zKey. zClass may be NULL but + zKey may not. + */ + const zKeyForStorage = (store, zClass, zKey) => { + return zClass && store.keyPrefix ? kvvfsMakeKey(zClass, zKey) : zKey; + }; + const jsKeyForStorage = (store, zClass, zKey) => wasm.cstrToJs(zKeyForStorage(store, zClass, zKey)); + const storageGetDbSize = (store) => +store.storage.getItem(store.keyPrefix + "sz"); + /** + sqlite3_file pointers => objects, each of which has: + + .file = KVVfsFile instance + + .jzClass = JS-string form of f.$zClass + + .storage = Storage object. It is shared between a db and its + journal. + */ + const pFileHandles = /* @__PURE__ */ new Map(); + /** + Original WASM functions for methods we partially override. + */ + const originalMethods = { + vfs: Object.create(null), + ioDb: Object.create(null), + ioJrnl: Object.create(null) + }; + const pVfs = new capi.sqlite3_vfs(kvvfsMethods.$pVfs); + const pIoDb = new capi.sqlite3_io_methods(kvvfsMethods.$pIoDb); + const pIoJrnl = new capi.sqlite3_io_methods(kvvfsMethods.$pIoJrnl); + const recordHandler = Object.create(null); + const kvvfsInternal = Object.assign(Object.create(null), { + pFileHandles, + cache, + storageForZClass, + KVVfsStorage, + disablePageSizeChange: true + }); + if (kvvfs.log) kvvfs.internal = kvvfsInternal; + /** + Implementations for members of the object referred to by + sqlite3__wasm_kvvfs_methods(). We swap out some native + implementations with these so that we can use JS Storage for + their backing store. + */ + const methodOverrides = { + recordHandler: { + xRcrdRead: (zClass, zKey, zBuf, nBuf) => { + try { + const jzClass = wasm.cstrToJs(zClass); + const store = storageForZClass(jzClass); + if (!store) return -1; + const jXKey = jsKeyForStorage(store, zClass, zKey); + kvvfs?.log?.xRcrdRead && warn("xRcrdRead", jzClass, jXKey, nBuf, store); + const jV = store.storage.getItem(jXKey); + if (null === jV) return -1; + const nV = jV.length; + if (nBuf <= 0) return nV; + else if (1 === nBuf) { + wasm.poke(zBuf, 0); + return nV; + } + if (nBuf + 1 < nV) toss3(capi.SQLITE_RANGE, "xRcrdRead()", jzClass, jXKey, "input buffer is too small: need", nV, "but have", nBuf); + const zV = cache.memBuffer(0); + const heap = wasm.heap8(); + let i; + for (i = 0; i < nV; ++i) heap[wasm.ptr.add(zV, i)] = jV.codePointAt(i) & 255; + heap.copyWithin(Number(zBuf), Number(zV), wasm.ptr.addn(zV, i)); + heap[wasm.ptr.add(zBuf, nV)] = 0; + return nBuf; + } catch (e) { + error("kvrecordRead()", e); + cache.setError(e); + return -2; + } + }, + xRcrdWrite: (zClass, zKey, zData) => { + try { + const store = storageForZClass(zClass); + const jxKey = jsKeyForStorage(store, zClass, zKey); + const jData = wasm.cstrToJs(zData); + kvvfs?.log?.xRcrdWrite && warn("xRcrdWrite", jxKey, store); + store.storage.setItem(jxKey, jData); + store.listeners && notifyListeners("write", store, jxKey, jData); + return 0; + } catch (e) { + error("kvrecordWrite()", e); + return cache.setError(e, capi.SQLITE_IOERR); + } + }, + xRcrdDelete: (zClass, zKey) => { + try { + const store = storageForZClass(zClass); + const jxKey = jsKeyForStorage(store, zClass, zKey); + kvvfs?.log?.xRcrdDelete && warn("xRcrdDelete", jxKey, store); + store.storage.removeItem(jxKey); + store.listeners && notifyListeners("delete", store, jxKey); + return 0; + } catch (e) { + error("kvrecordDelete()", e); + return cache.setError(e, capi.SQLITE_IOERR); + } + } + }, + vfs: { + xOpen: function(pProtoVfs, zName, pProtoFile, flags, pOutFlags) { + cache.popError(); + let zToFree; + try { + if (!zName) { + zToFree = wasm.allocCString("" + pProtoFile + "." + (Math.random() * 1e5 | 0)); + zName = zToFree; + } + const jzClass = wasm.cstrToJs(zName); + kvvfs?.log?.xOpen && debug("xOpen", jzClass, "flags =", flags); + validateStorageName(jzClass, true); + if (flags & (capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_TEMP_DB | capi.SQLITE_OPEN_TRANSIENT_DB) && cache.rxJournalSuffix.test(jzClass)) toss3(capi.SQLITE_ERROR, "DB files may not have a '-journal' suffix."); + let s = storageForZClass(jzClass); + if (!s && !(flags & capi.SQLITE_OPEN_CREATE)) toss3(capi.SQLITE_ERROR, "Storage not found:", jzClass); + const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile, flags, pOutFlags); + if (rc) return rc; + let deleteAt0 = !!(capi.SQLITE_OPEN_DELETEONCLOSE & flags); + if (wasm.isPtr(arguments[1])) { + if (capi.sqlite3_uri_boolean(zName, "delete-on-close", 0)) deleteAt0 = true; + } + const f = new KVVfsFile(pProtoFile); + util.assert(f.$zClass, "Missing f.$zClass"); + f.addOnDispose(zToFree); + zToFree = void 0; + if (s) { + ++s.refc; + s.files.push(f); + wasm.poke32(pOutFlags, flags); + } else { + wasm.poke32(pOutFlags, flags | capi.SQLITE_OPEN_CREATE); + util.assert(!f.$isJournal, "Opening a journal before its db? " + jzClass); + const nm = jzClass.replace(cache.rxJournalSuffix, ""); + s = newStorageObj(nm); + installStorageAndJournal(s); + s.files.push(f); + s.deleteAtRefc0 = deleteAt0; + kvvfs?.log?.xOpen && debug("xOpen installed storage handle [", nm, nm + "-journal", "]", s); + } + pFileHandles.set(pProtoFile, { + store: s, + file: f, + jzClass + }); + s.listeners && notifyListeners("open", s, s.files.length); + return 0; + } catch (e) { + warn("xOpen:", e); + return cache.setError(e); + } finally { + zToFree && wasm.dealloc(zToFree); + } + }, + xDelete: function(pVfs, zName, iSyncFlag) { + cache.popError(); + try { + const jzName = wasm.cstrToJs(zName); + if (cache.rxJournalSuffix.test(jzName)) recordHandler.xRcrdDelete(zName, cache.zKeyJrnl); + return 0; + } catch (e) { + warn("xDelete", e); + return cache.setError(e); + } + }, + xAccess: function(pProtoVfs, zPath, flags, pResOut) { + cache.popError(); + try { + const s = storageForZClass(zPath); + const jzPath = s?.jzClass || wasm.cstrToJs(zPath); + if (kvvfs?.log?.xAccess) debug("xAccess", jzPath, "flags =", flags, "*pResOut =", wasm.peek32(pResOut), "store =", s); + if (!s) + /** The xAccess method returns [SQLITE_OK] on success or some + ** non-zero error code if there is an I/O error or if the name of + ** the file given in the second argument is illegal. + */ + try { + validateStorageName(jzPath); + } catch (e) { + wasm.poke32(pResOut, 0); + return 0; + } + if (s) { + const key = s.keyPrefix + (cache.rxJournalSuffix.test(jzPath) ? "jrnl" : "1"); + const res = s.storage.getItem(key) ? 0 : 1; + wasm.poke32(pResOut, res); + } else wasm.poke32(pResOut, 0); + return 0; + } catch (e) { + error("xAccess", e); + return cache.setError(e); + } + }, + xRandomness: function(pVfs, nOut, pOut) { + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; + return nOut; + }, + xGetLastError: function(pVfs, nOut, pOut) { + const e = cache.popError(); + debug("xGetLastError", e); + if (e) { + const scope = wasm.scopedAllocPush(); + try { + const [cMsg, n] = wasm.scopedAllocCString(e.message, true); + wasm.cstrncpy(pOut, cMsg, nOut); + if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); + debug("set xGetLastError", e.message); + return e.resultCode | 0 || capi.SQLITE_IOERR; + } catch (e) { + return capi.SQLITE_NOMEM; + } finally { + wasm.scopedAllocPop(scope); + } + } + return 0; + } + }, + ioDb: { + xClose: function(pFile) { + cache.popError(); + try { + const h = pFileHandles.get(pFile); + kvvfs?.log?.xClose && debug("xClose", pFile, h); + if (h) { + pFileHandles.delete(pFile); + const s = h.store; + s.files = s.files.filter((v) => v !== h.file); + if (--s.refc <= 0 && s.deleteAtRefc0) deleteStorage(s); + originalMethods.ioDb.xClose(pFile); + h.file.dispose(); + s.listeners && notifyListeners("close", s, s.files.length); + } + return 0; + } catch (e) { + error("xClose", e); + return cache.setError(e); + } + }, + xFileControl: function(pFile, opId, pArg) { + cache.popError(); + try { + const h = pFileHandles.get(pFile); + util.assert(h, "Missing KVVfsFile handle"); + kvvfs?.log?.xFileControl && debug("xFileControl", h, "op =", opId); + if (opId === capi.SQLITE_FCNTL_PRAGMA && kvvfsInternal.disablePageSizeChange) { + const zName = wasm.peekPtr(wasm.ptr.add(pArg, wasm.ptr.size)); + if ("page_size" === wasm.cstrToJs(zName)) { + kvvfs?.log?.xFileControl && debug("xFileControl pragma", wasm.cstrToJs(zName)); + const zVal = wasm.peekPtr(wasm.ptr.add(pArg, 2 * wasm.ptr.size)); + if (zVal) { + kvvfs?.log?.xFileControl && warn("xFileControl pragma", h, "NOT setting page size to", wasm.cstrToJs(zVal)); + h.file.$szPage = -1; + return 0; + } else if (h.file.$szPage > 0) { + kvvfs?.log?.xFileControl && warn("xFileControl", h, "getting page size", h.file.$szPage); + wasm.pokePtr(pArg, wasm.allocCString("" + h.file.$szPage)); + return 0; + } + } + } + const rc = originalMethods.ioDb.xFileControl(pFile, opId, pArg); + if (0 == rc && capi.SQLITE_FCNTL_SYNC === opId) h.store.listeners && notifyListeners("sync", h.store, false); + return rc; + } catch (e) { + error("xFileControl", e); + return cache.setError(e); + } + }, + xSync: function(pFile, flags) { + cache.popError(); + try { + const h = pFileHandles.get(pFile); + kvvfs?.log?.xSync && debug("xSync", h); + util.assert(h, "Missing KVVfsFile handle"); + const rc = originalMethods.ioDb.xSync(pFile, flags); + if (0 == rc && h.store.listeners) notifyListeners("sync", h.store, true); + return rc; + } catch (e) { + error("xSync", e); + return cache.setError(e); + } + }, + xRead: function(pFile, pTgt, n, iOff64) { + cache.popError(); + try { + if (kvvfs?.log?.xRead) { + const h = pFileHandles.get(pFile); + util.assert(h, "Missing KVVfsFile handle"); + debug("xRead", n, iOff64, h); + } + return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); + } catch (e) { + error("xRead", e); + return cache.setError(e); + } + }, + xWrite: function(pFile, pSrc, n, iOff64) { + cache.popError(); + try { + if (kvvfs?.log?.xWrite) { + const h = pFileHandles.get(pFile); + util.assert(h, "Missing KVVfsFile handle"); + debug("xWrite", n, iOff64, h); + } + return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); + } catch (e) { + error("xWrite", e); + return cache.setError(e); + } + } + }, + ioJrnl: { xClose: true } + }; + debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, kvvfsMethods, capi.sqlite3_file.structInfo, KVVfsFile.structInfo); + try { + util.assert(cache.buffer.n > 1024 * 129, "Heap buffer is not large enough"); + for (const e of Object.entries(methodOverrides.recordHandler)) { + const k = e[0], f = e[1]; + recordHandler[k] = f; + kvvfsMethods[kvvfsMethods.memberKey(k)] = wasm.installFunction(kvvfsMethods.memberSignature(k), f); + } + for (const e of Object.entries(methodOverrides.vfs)) { + const k = e[0], f = e[1], km = pVfs.memberKey(k), member = pVfs.structInfo.members[k] || util.toss("Missing pVfs.structInfo[", k, "]"); + originalMethods.vfs[k] = wasm.functionEntry(pVfs[km]); + pVfs[km] = wasm.installFunction(member.signature, f); + } + for (const e of Object.entries(methodOverrides.ioDb)) { + const k = e[0], f = e[1], km = pIoDb.memberKey(k); + originalMethods.ioDb[k] = wasm.functionEntry(pIoDb[km]) || util.toss("Missing native pIoDb[", km, "]"); + pIoDb[km] = wasm.installFunction(pIoDb.memberSignature(k), f); + } + for (const e of Object.entries(methodOverrides.ioJrnl)) { + const k = e[0], f = e[1], km = pIoJrnl.memberKey(k); + originalMethods.ioJrnl[k] = wasm.functionEntry(pIoJrnl[km]) || util.toss("Missing native pIoJrnl[", km, "]"); + if (true === f) pIoJrnl[km] = pIoDb[km] || util.toss("Missing copied pIoDb[", km, "]"); + else pIoJrnl[km] = wasm.installFunction(pIoJrnl.memberSignature(k), f); + } + } finally { + kvvfsMethods.dispose(); + pVfs.dispose(); + pIoDb.dispose(); + pIoJrnl.dispose(); + } + /** + Clears all storage used by the kvvfs DB backend, deleting any + DB(s) stored there. + + Its argument must be the name of a kvvfs storage object: + + - 'session' + - 'local' + - '' - see below. + - A transient kvvfs storage object name. + + In the first two cases, only sessionStorage resp. localStorage is + cleared. An empty string resolves to both 'local' and 'session' + storage. + + Returns the number of entries cleared. + + As of kvvfs version 2: + + This API is available in Worker threads but does not have access + to localStorage or sessionStorage in them. Prior versions did not + include this API in Worker threads. + + Differences in this function in version 2: + + - It accepts an arbitrary storage name. In v1 this was a silent + no-op for any names other than ('local','session',''). + + - It throws if a db currently has the storage opened UNLESS the + storage object is localStorage or sessionStorage. That version 1 + did not throw for this case was due to an architectural + limitation which has since been overcome, but removal of + JsStorageDb.prototype.clearStorage() would be a backwards compatibility + break, so this function permits wiping the storage for those two + cases even if they are opened. Use with case. + */ + const sqlite3_js_kvvfs_clear = function callee(which) { + if ("" === which) return callee("local") + callee("session"); + const store = storageForZClass(which); + if (!store) return 0; + if (store.files.length) if (globalThis.localStorage === store.storage || globalThis.sessionStorage === store.storage) {} else toss3(capi.SQLITE_ACCESS, "Cannot clear in-use database storage."); + const s = store.storage; + const toRm = []; + let i, n = s.length; + for (i = 0; i < n; ++i) { + const k = s.key(i); + if (!store.keyPrefix || k.startsWith(store.keyPrefix)) toRm.push(k); + } + toRm.forEach((kk) => s.removeItem(kk)); + return toRm.length; + }; + /** + This routine estimates the approximate amount of + storage used by the given kvvfs back-end. + + Its arguments are as documented for sqlite3_js_kvvfs_clear(), + only the operation this performs is different. + + The returned value is twice the "length" value of every matching + key and value, noting that JavaScript stores each character in 2 + bytes. + + The returned size is not authoritative from the perspective of + how much data can fit into localStorage and sessionStorage, as + the precise algorithms for determining those limits are + unspecified and may include per-entry overhead invisible to + clients. + */ + const sqlite3_js_kvvfs_size = function callee(which) { + if ("" === which) return callee("local") + callee("session"); + const store = storageForZClass(which); + if (!store) return 0; + const s = store.storage; + let i, sz = 0; + for (i = 0; i < s.length; ++i) { + const k = s.key(i); + if (!store.keyPrefix || k.startsWith(store.keyPrefix)) { + sz += k.length; + sz += s.getItem(k).length; + } + } + return sz * 2; + }; + /** + Exports a kvvfs storage object to an object, optionally + JSON-friendly. + + Usages: + + thisfunc(storageName); + thisfunc(options); + + In the latter case, the options object must be an object with + the following properties: + + - "name" (string) required. The storage to export. + + - "decodePages" (bool=false). If true, the .pages result property + holdes Uint8Array objects holding the raw binary-format db + pages. The default is to use kvvfs-encoded string pages + (JSON-friendly). + + - "includeJournal" (bool=false). If true and the db has a current + journal, it is exported as well. (Kvvfs journals are stored as a + single record within the db's storage object.) + + The returned object is structured as follows... + + - "name": the name of the storage. This is 'local' or 'session' + for localStorage resp. sessionStorage, and an arbitrary name for + transient storage. This propery may be changed before passing + this object to sqlite3_js_kvvfs_import() in order to + import into a different storage object. + + - "timestamp": the time this function was called, in Unix + epoch milliseconds. + + - "size": the unencoded db size. + + - "journal": if options.includeJournal is true and this db has a + journal, it is stored as a string here, otherwise this property + is not set. + + - "pages": An array holding the raw encoded db pages in their + proper order. + + Throws if this db is not opened. + + The encoding of the underlying database is not part of this + interface - it is simply passed on as-is. Interested parties are + directed to src/os_kv.c in the SQLite source tree, with the + caveat that that code also does not offer a public interface. + i.e. the encoding is a private implementation detail of kvvfs. + The format may be changed in the future but kvvfs will continue + to support the current form. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_export = function callee(...args) { + let opt; + if (1 === args.length && "object" === typeof args[0]) opt = args[0]; + else if (args.length) opt = Object.assign(Object.create(null), { name: args[0] }); + const store = opt ? storageForZClass(opt.name) : null; + if (!store) toss3(capi.SQLITE_NOTFOUND, "There is no kvvfs storage named", opt?.name); + const s = store.storage; + const rc = Object.assign(Object.create(null), { + name: store.jzClass, + timestamp: Date.now(), + pages: [] + }); + const pages = Object.create(null); + const keyPrefix = store.keyPrefix; + const rxTail = keyPrefix ? /^kvvfs-[^-]+-(\w+)/ : void 0; + let i = 0, n = s.length; + for (; i < n; ++i) { + const k = s.key(i); + if (!keyPrefix || k.startsWith(keyPrefix)) { + let kk = (keyPrefix ? rxTail.exec(k) : void 0)?.[1] ?? k; + switch (kk) { + case "jrnl": + if (opt.includeJournal) rc.journal = s.getItem(k); + break; + case "sz": + rc.size = +s.getItem(k); + break; + default: + kk = +kk; + if (!util.isInt32(kk) || kk <= 0) toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: " + k); + if (opt.decodePages) { + const spg = s.getItem(k), n = spg.length, z = cache.memBuffer(0), zDec = cache.memBuffer(1), heap = wasm.heap8u(); + let i = 0; + for (; i < n; ++i) heap[wasm.ptr.add(z, i)] = spg.codePointAt(i) & 255; + heap[wasm.ptr.add(z, i)] = 0; + const nDec = kvvfsDecode(z, zDec, cache.buffer.n); + pages[kk] = heap.slice(Number(zDec), wasm.ptr.addn(zDec, nDec)); + } else pages[kk] = s.getItem(k); + break; + } + } + } + if (opt.decodePages) cache.memBufferFree(1); + Object.keys(pages).map((v) => +v).sort().forEach((v) => rc.pages.push(pages[v])); + return rc; + }; + /** + The counterpart of sqlite3_js_kvvfs_export(). Its + argument must be the result of that function() or + a compatible one. + + This either replaces the contents of an existing transient + storage object or installs one named exp.name, setting + the storage's db contents to that of the exp object. + + Throws on error. Error conditions include: + + - The given storage object is currently opened by any db. + Performing this page-by-page import would invoke undefined + behavior on them. + + - Malformed input object. + + If it throws after starting the import then it clears the storage + before returning, to avoid leaving the db in an undefined + state. It may throw for any of the above-listed conditions before + reaching that step, in which case the db is not modified. If + exp.name refers to a new storage name then if it throws, the name + does not get installed. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_import = function(exp, overwrite = false) { + if (!exp?.timestamp || !exp.name || void 0 === exp.size || !Array.isArray(exp.pages)) toss3(capi.SQLITE_MISUSE, "Malformed export object."); + else if (!exp.size || exp.size !== (exp.size | 0) || exp.size >= 2147483647) toss3(capi.SQLITE_RANGE, "Invalid db size: " + exp.size); + validateStorageName(exp.name); + let store = storageForZClass(exp.name); + const isNew = !store; + if (store) { + if (!overwrite) toss3(capi.SQLITE_ACCESS, "Storage '" + exp.name + "' already exists and", "overwrite was not specified."); + else if (!store.files || !store.jzClass) toss3(capi.SQLITE_ERROR, "Internal storage object", exp.name, "seems to be malformed."); + else if (store.files.length) toss3(capi.SQLITE_IOERR_ACCESS, "Cannot import db storage while it is in use."); + sqlite3_js_kvvfs_clear(exp.name); + } else store = newStorageObj(exp.name); + const keyPrefix = kvvfsKeyPrefix(exp.name); + let zEnc; + try { + const s = store.storage; + s.setItem(keyPrefix + "sz", exp.size); + if (exp.journal) s.setItem(keyPrefix + "jrnl", exp.journal); + if (exp.pages[0] instanceof Uint8Array) exp.pages.forEach((u, ndx) => { + const n = u.length; + zEnc ??= cache.memBuffer(1); + const zBin = cache.memBuffer(0), heap = wasm.heap8u(); + heap.set(u, Number(zBin)); + heap[wasm.ptr.addn(zBin, n)] = 0; + const rc = kvvfsEncode(zBin, n, zEnc); + util.assert(rc < cache.buffer.n, "Impossibly long output - possibly smashed the heap"); + util.assert(0 === wasm.peek8(wasm.ptr.add(zEnc, rc)), "Expecting NUL-terminated encoded output"); + const jenc = wasm.cstrToJs(zEnc); + s.setItem(keyPrefix + (ndx + 1), jenc); + }); + else if (exp.pages[0]) exp.pages.forEach((v, ndx) => s.setItem(keyPrefix + (ndx + 1), v)); + if (isNew) installStorageAndJournal(store); + } catch { + if (!isNew) try { + sqlite3_js_kvvfs_clear(exp.name); + } catch (ee) {} + } finally { + if (zEnc) cache.memBufferFree(1); + } + return this; + }; + /** + If no kvvfs storage exists with the given name, one is + installed. If one exists, its reference count is increased so + that it won't be freed by the closing of a database or journal + file. + + Throws if the name is not valid for a new storage object. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_reserve = function(name) { + let store = storageForZClass(name); + if (store) { + ++store.refc; + return; + } + validateStorageName(name); + installStorageAndJournal(newStorageObj(name)); + }; + /** + Conditionally "unlinks" a kvvfs storage object, reducing its + reference count by 1. + + This is a no-op if name ends in "-journal" or refers to a + built-in storage object. + + It will not lower the refcount below the number of + currently-opened db/journal files for the storage (so that it + cannot delete it out from under them). + + If the refcount reaches 0 then the storage object is + removed. + + Returns true if it reduces the refcount, else false. A result of + true does not necessarily mean that the storage unit was removed, + just that its refcount was lowered. Similarly, a result of false + does not mean that the storage is removed - it may still have + opened handles. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_unlink = function(name) { + const store = storageForZClass(name); + if (!store || kvvfsIsPersistentName(store.jzClass) || isBuiltinName(store.jzClass) || cache.rxJournalSuffix.test(name)) return false; + if (store.refc > store.files.length || 0 === store.files.length) { + if (--store.refc <= 0) deleteStorage(store); + return true; + } + return false; + }; + /** + Adds an event listener to a kvvfs storage object. The idea is + that this can be used to asynchronously back up one kvvfs storage + object to another or another channel entirely. (The caveat in the + latter case is that kvvfs's format is not readily consumable by + downstream code.) + + Its argument must be an object with the following properties: + + - storage: the name of the kvvfs storage object. + + - reserve [=false]: if true, sqlite3_js_kvvfs_reserve() is used + to ensure that the storage exists if it does not already. + If this is false and the storage does not exist then an + exception is thrown. + + - events: an object which may have any of the following + callback function properties: open, close, write, delete. + + - decodePages [=false]: if true, write events will receive each + db page write in the form of a Uint8Array holding the raw binary + db page. The default is to emit the kvvfs-format page because it + requires no extra work, we already have it in hand, and it's + often smaller. It's not great for interchange, though. + + - includeJournal [=false]: if true, writes and deletes of + "jrnl" records are included. If false, no events are sent + for journal updates. + + Passing the same object to sqlite3_js_kvvfs_unlisten() will + remove the listener. + + Each one of the events callbacks will be called asynchronously + when the given storage performs those operations. They may be + asynchronous functions but are not required to be (the events are + fired async either way, but making the event callbacks async may + be advantageous when multiple listeners are involved). All + exceptions, including those via Promises, are ignored but may (or + may not) trigger warning output on the console. + + Each callback gets passed a single object with the following + properties: + + .type = the same as the name of the callback + + .storageName = the name of the storage object + + .data = callback-dependent: + + - 'open' and 'close' get an integer, the number of + currently-opened handles on the storage. + + - 'write' gets a length-two array holding the key and value which + were written. The key is always a string, even if it's a db page + number. For db-page records, the value's type depends on + opt.decodePages. All others, including the journal, are strings. + (The journal, being a kvvfs-specific format, is delivered in + that same JSON-friendly format.) More details below. + + - 'delete' gets the string-type key of the deleted record. + + - 'sync' gets a boolean value: true if it was triggered by db + file's xSync(), false if it was triggered by xFileControl(). The + latter triggers before the xSync() and also triggers if the DB + has PRAGMA SYNCHRONOUS=OFF (in which case xSync() is not + triggered). + + The key/value arguments to 'write', and key argument to 'delete', + are in one of the following forms: + + - 'sz' = the unencoded db size as a string. This specific key is + key is never deleted, so is only ever passed to 'write' events. + + - 'jrnl' = the current db journal as a kvvfs-encoded string. This + journal format is not useful anywhere except in the kvvfs + internals. These events are not fired if opt.includeJournal is + false. + + - '[1-9][0-9]*' (a db page number) = Its type depends on + opt.decodePages. These may be written and deleted in arbitrary + order. + + Design note: JS has StorageEvents but only in the main thread, + which is why the listeners are not based on that. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_listen = function(opt) { + if (!opt || "object" !== typeof opt) toss3(capi.SQLITE_MISUSE, "Expecting a listener object."); + let store = storageForZClass(opt.storage); + if (!store) if (opt.storage && opt.reserve) { + sqlite3_js_kvvfs_reserve(opt.storage); + store = storageForZClass(opt.storage); + util.assert(store, "Unexpectedly cannot fetch reserved storage " + opt.storage); + } else toss3(capi.SQLITE_NOTFOUND, "No such storage:", opt.storage); + if (opt.events) (store.listeners ??= []).push(opt); + }; + /** + Removes the kvvfs event listeners for the given options + object. It must be passed the same object instance which was + passed to sqlite3_js_kvvfs_listen(). + + This has no side effects if opt is invalid or is not a match for + any listeners. + + Return true if it unregisters its argument, else false. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_unlisten = function(opt) { + const store = storageForZClass(opt?.storage); + if (store?.listeners && opt.events) { + const n = store.listeners.length; + store.listeners = store.listeners.filter((v) => v !== opt); + const rc = n > store.listeners.length; + if (!store.listeners.length) store.listeners = void 0; + return rc; + } + return false; + }; + sqlite3.kvvfs.reserve = sqlite3_js_kvvfs_reserve; + sqlite3.kvvfs.import = sqlite3_js_kvvfs_import; + sqlite3.kvvfs.export = sqlite3_js_kvvfs_export; + sqlite3.kvvfs.unlink = sqlite3_js_kvvfs_unlink; + sqlite3.kvvfs.listen = sqlite3_js_kvvfs_listen; + sqlite3.kvvfs.unlisten = sqlite3_js_kvvfs_unlisten; + sqlite3.kvvfs.exists = (name) => !!storageForZClass(name); + sqlite3.kvvfs.estimateSize = sqlite3_js_kvvfs_size; + sqlite3.kvvfs.clear = sqlite3_js_kvvfs_clear; + if (globalThis.Storage) { + /** + Prior to version 2, kvvfs was only available in the main + thread. We retain that for the v1 APIs, exposing them only in + the main UI thread. As of version 2, kvvfs is available in all + threads but only via its v2 interface (sqlite3.kvvfs). + + These versions have a default argument value of "" which the v2 + versions lack. + */ + capi.sqlite3_js_kvvfs_size = (which = "") => sqlite3_js_kvvfs_size(which); + capi.sqlite3_js_kvvfs_clear = (which = "") => sqlite3_js_kvvfs_clear(which); + } + if (sqlite3.oo1?.DB) { + /** + Functionally equivalent to DB(storageName,'c','kvvfs') except + that it throws if the given storage name is not one of 'local' + or 'session'. + + As of version 3.46, the argument may optionally be an options + object in the form: + + { + filename: 'session'|'local', + ... etc. (all options supported by the DB ctor) + } + + noting that the 'vfs' option supported by main DB + constructor is ignored here: the vfs is always 'kvvfs'. + */ + const DB = sqlite3.oo1.DB; + sqlite3.oo1.JsStorageDb = function(storageName = sqlite3.oo1.JsStorageDb.defaultStorageName) { + const opt = DB.dbCtorHelper.normalizeArgs(...arguments); + opt.vfs = "kvvfs"; + switch (opt.filename) { + case ":sessionStorage:": + opt.filename = "session"; + break; + case ":localStorage:": + opt.filename = "local"; + break; + } + const m = /(file:(\/\/)?)([^?]+)/.exec(opt.filename); + validateStorageName(m ? m[3] : opt.filename); + DB.dbCtorHelper.call(this, opt); + }; + sqlite3.oo1.JsStorageDb.defaultStorageName = cache.storagePool.session ? "session" : nameOfThisThreadStorage; + const jdb = sqlite3.oo1.JsStorageDb; + jdb.prototype = Object.create(DB.prototype); + jdb.clearStorage = sqlite3_js_kvvfs_clear; + /** + DEPRECATED: the inherited method of this name (as opposed to + the "static" class method) is deprecated with version 2 of + kvvfs. This function will, for backwards comaptibility, + continue to work with localStorage and sessionStorage, but will + throw for all other storage because they are opened. Version 1 + was not capable of recognizing that the storage was opened so + permitted wiping it out at any time, but that was arguably a + bug. + + Clears this database instance's storage or throws if this + instance has been closed. Returns the number of + database pages which were cleaned up. + */ + jdb.prototype.clearStorage = function() { + return jdb.clearStorage(this.affirmOpen().dbFilename(), true); + }; + /** Equivalent to sqlite3_js_kvvfs_size(). */ + jdb.storageSize = sqlite3_js_kvvfs_size; + /** + Returns the _approximate_ number of bytes this database takes + up in its storage or throws if this instance has been closed. + */ + jdb.prototype.storageSize = function() { + return jdb.storageSize(this.affirmOpen().dbFilename(), true); + }; + } + if (sqlite3.__isUnderTest && sqlite3.vtab) { + /** + An eponymous vtab for inspecting the kvvfs state. This is only + intended for use in testing and development, not part of the + public API. + */ + const cols = Object.assign(Object.create(null), { + rowid: { type: "INTEGER" }, + name: { type: "TEXT" }, + nRef: { type: "INTEGER" }, + nOpen: { type: "INTEGER" }, + isTransient: { type: "INTEGER" }, + dbSize: { type: "INTEGER" } + }); + Object.keys(cols).forEach((v, i) => cols[v].colId = i); + const VT = sqlite3.vtab; + const ProtoCursor = Object.assign(Object.create(null), { row: function() { + return cache.storagePool[this.names[this.rowid]]; + } }); + Object.assign(Object.create(ProtoCursor), { + rowid: 0, + names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) + }); + const cursorState = function(cursor, reset) { + const o = cursor instanceof capi.sqlite3_vtab_cursor ? cursor : VT.xCursor.get(cursor); + if (reset || !o.vTabState) o.vTabState = Object.assign(Object.create(ProtoCursor), { + rowid: 0, + names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) + }); + return o.vTabState; + }; + const dbg = () => {}; + const theModule = function f() { + return f.mod ??= new sqlite3.capi.sqlite3_module().setupModule({ + catchExceptions: true, + methods: { + xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr) { + dbg("xConnect"); + try { + const xcol = []; + Object.keys(cols).forEach((k) => { + xcol.push(k + " " + cols[k].type); + }); + const rc = capi.sqlite3_declare_vtab(pDb, "CREATE TABLE ignored(" + xcol.join(",") + ")"); + if (0 === rc) { + const t = VT.xVtab.create(ppVtab); + util.assert(t === VT.xVtab.get(wasm.peekPtr(ppVtab)), "output pointer check failed"); + } + return rc; + } catch (e) { + return VT.xErrror("xConnect", e, capi.SQLITE_ERROR); + } + }, + xCreate: wasm.ptr.null, + xDisconnect: function(pVtab) { + dbg("xDisconnect", ...arguments); + VT.xVtab.dispose(pVtab); + return 0; + }, + xOpen: function(pVtab, ppCursor) { + dbg("xOpen", ...arguments); + VT.xCursor.create(ppCursor); + return 0; + }, + xClose: function(pCursor) { + dbg("xClose", ...arguments); + const c = VT.xCursor.unget(pCursor); + delete c.vTabState; + c.dispose(); + return 0; + }, + xNext: function(pCursor) { + dbg("xNext", ...arguments); + const c = VT.xCursor.get(pCursor); + ++cursorState(c).rowid; + return 0; + }, + xColumn: function(pCursor, pCtx, iCol) { + dbg("xColumn", ...arguments); + const st = cursorState(pCursor); + const store = st.row(); + util.assert(store, "Unexpected xColumn call"); + switch (iCol) { + case cols.rowid.colId: + capi.sqlite3_result_int(pCtx, st.rowid); + break; + case cols.name.colId: + capi.sqlite3_result_text(pCtx, store.jzClass, -1, capi.SQLITE_TRANSIENT); + break; + case cols.nRef.colId: + capi.sqlite3_result_int(pCtx, store.refc); + break; + case cols.nOpen.colId: + capi.sqlite3_result_int(pCtx, store.files.length); + break; + case cols.isTransient.colId: + capi.sqlite3_result_int(pCtx, !!store.deleteAtRefc0); + break; + case cols.dbSize.colId: + capi.sqlite3_result_int(pCtx, storageGetDbSize(store)); + break; + default: + capi.sqlite3_result_error(pCtx, "Invalid column id: " + iCol); + return capi.SQLITE_RANGE; + } + return 0; + }, + xRowid: function(pCursor, ppRowid64) { + dbg("xRowid", ...arguments); + const st = cursorState(pCursor); + VT.xRowid(ppRowid64, st.rowid); + return 0; + }, + xEof: function(pCursor) { + const st = cursorState(pCursor); + dbg("xEof?=" + !st.row(), ...arguments); + return !st.row(); + }, + xFilter: function(pCursor, idxNum, idxCStr, argc, argv) { + dbg("xFilter", ...arguments); + cursorState(pCursor, true); + return 0; + }, + xBestIndex: function(pVtab, pIdxInfo) { + dbg("xBestIndex", ...arguments); + const pii = new capi.sqlite3_index_info(pIdxInfo); + pii.$estimatedRows = cache.storagePool.size; + pii.$estimatedCost = 1; + pii.dispose(); + return 0; + } + } + }); + }; + sqlite3.kvvfs.create_module = function(pDb, name = "sqlite_kvvfs") { + return capi.sqlite3_create_module(pDb, name, theModule(), wasm.ptr.null); + }; + } + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + /** + installOpfsVfs() returns a Promise which, on success, installs an + sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs + which accept a VFS. It is intended to be called via + sqlite3ApiBootstrap.initializers or an equivalent mechanism. + + The installed VFS uses the Origin-Private FileSystem API for + all file storage. On error it is rejected with an exception + explaining the problem. Reasons for rejection include, but are + not limited to: + + - The counterpart Worker (see below) could not be loaded. + + - The environment does not support OPFS. That includes when + this function is called from the main window thread. + + Significant notes and limitations: + + - The OPFS features used here are only available in dedicated Worker + threads. This file tries to detect that case, resulting in a + rejected Promise if those features do not seem to be available. + + - It requires the SharedArrayBuffer and Atomics classes, and the + former is only available if the HTTP server emits the so-called + COOP and COEP response headers. These features are required for + proxying OPFS's synchronous API via the synchronous interface + required by the sqlite3_vfs API. + + - This function may only be called a single time. When called, this + function removes itself from the sqlite3 object. + + All arguments to this function are for internal/development purposes + only. They do not constitute a public API and may change at any + time. + + The argument may optionally be a plain object with the following + configuration options: + + - proxyUri: name of the async proxy JS file. + + - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables + logging of errors. 2 enables logging of warnings and errors. 3 + additionally enables debugging info. Logging is performed + via the sqlite3.config.{log|warn|error}() functions. + + - sanityChecks (=false): if true, some basic sanity tests are run on + the OPFS VFS API after it's initialized, before the returned + Promise resolves. This is only intended for testing and + development of the VFS, not client-side use. + + On success, the Promise resolves to the top-most sqlite3 namespace + object and that object gets a new object installed in its + `opfs` property, containing several OPFS-specific utilities. + */ + const installOpfsVfs = function callee(options) { + if (!globalThis.SharedArrayBuffer || !globalThis.Atomics) return Promise.reject(/* @__PURE__ */ new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. The server must emit the COOP/COEP response headers to enable those. See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep")); + else if ("undefined" === typeof WorkerGlobalScope) return Promise.reject(/* @__PURE__ */ new Error("The OPFS sqlite3_vfs cannot run in the main thread because it requires Atomics.wait().")); + else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); + if (!options || "object" !== typeof options) options = Object.create(null); + const urlParams = new URL(globalThis.location.href).searchParams; + if (urlParams.has("opfs-disable")) return Promise.resolve(sqlite3); + if (void 0 === options.verbose) options.verbose = urlParams.has("opfs-verbose") ? +urlParams.get("opfs-verbose") || 2 : 1; + if (void 0 === options.sanityChecks) options.sanityChecks = urlParams.has("opfs-sanity-check"); + if (void 0 === options.proxyUri) options.proxyUri = callee.defaultProxyUri; + if ("function" === typeof options.proxyUri) options.proxyUri = options.proxyUri(); + return new Promise(function(promiseResolve_, promiseReject_) { + const loggers = [ + sqlite3.config.error, + sqlite3.config.warn, + sqlite3.config.log + ]; + const logImpl = (level, ...args) => { + if (options.verbose > level) loggers[level]("OPFS syncer:", ...args); + }; + const log = (...args) => logImpl(2, ...args); + const warn = (...args) => logImpl(1, ...args); + const error = (...args) => logImpl(0, ...args); + const toss = sqlite3.util.toss; + const capi = sqlite3.capi; + const util = sqlite3.util; + const wasm = sqlite3.wasm; + const sqlite3_vfs = capi.sqlite3_vfs; + const sqlite3_file = capi.sqlite3_file; + const sqlite3_io_methods = capi.sqlite3_io_methods; + /** + Generic utilities for working with OPFS. This will get filled out + by the Promise setup and, on success, installed as sqlite3.opfs. + + ACHTUNG: do not rely on these APIs in client code. They are + experimental and subject to change or removal as the + OPFS-specific sqlite3_vfs evolves. + */ + const opfsUtil = Object.create(null); + /** + Returns true if _this_ thread has access to the OPFS APIs. + */ + const thisThreadHasOPFS = () => { + return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory; + }; + /** + Not part of the public API. Solely for internal/development + use. + */ + opfsUtil.metrics = { + dump: function() { + let k, n = 0, t = 0, w = 0; + for (k in state.opIds) { + const m = metrics[k]; + n += m.count; + t += m.time; + w += m.wait; + m.avgTime = m.count && m.time ? m.time / m.count : 0; + m.avgWait = m.count && m.wait ? m.wait / m.count : 0; + } + sqlite3.config.log(globalThis.location.href, "metrics for", globalThis.location.href, ":", metrics, "\nTotal of", n, "op(s) for", t, "ms (incl. " + w + " ms of waiting on the async side)"); + sqlite3.config.log("Serialization metrics:", metrics.s11n); + W.postMessage({ type: "opfs-async-metrics" }); + }, + reset: function() { + let k; + const r = (m) => m.count = m.time = m.wait = 0; + for (k in state.opIds) r(metrics[k] = Object.create(null)); + let s = metrics.s11n = Object.create(null); + s = s.serialize = Object.create(null); + s.count = s.time = 0; + s = metrics.s11n.deserialize = Object.create(null); + s.count = s.time = 0; + } + }; + const opfsIoMethods = new sqlite3_io_methods(); + const opfsVfs = new sqlite3_vfs().addOnDispose(() => opfsIoMethods.dispose()); + let promiseWasRejected = void 0; + const promiseReject = (err) => { + promiseWasRejected = true; + opfsVfs.dispose(); + return promiseReject_(err); + }; + const promiseResolve = () => { + promiseWasRejected = false; + return promiseResolve_(sqlite3); + }; + const W = new Worker(new URL("sqlite3-opfs-async-proxy.js", import.meta.url)); + setTimeout(() => { + if (void 0 === promiseWasRejected) promiseReject(/* @__PURE__ */ new Error("Timeout while waiting for OPFS async proxy worker.")); + }, 4e3); + W._originalOnError = W.onerror; + W.onerror = function(err) { + error("Error initializing OPFS asyncer:", err); + promiseReject(/* @__PURE__ */ new Error("Loading OPFS async Worker failed for unknown reasons.")); + }; + const pDVfs = capi.sqlite3_vfs_find(null); + const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : null; + opfsIoMethods.$iVersion = 1; + opfsVfs.$iVersion = 2; + opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + opfsVfs.$mxPathname = 1024; + opfsVfs.$zName = wasm.allocCString("opfs"); + opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; + opfsVfs.addOnDispose("$zName", opfsVfs.$zName, "cleanup default VFS wrapper", () => dVfs ? dVfs.dispose() : null); + /** + Pedantic sidebar about opfsVfs.ondispose: the entries in that array + are items to clean up when opfsVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown order + of the wasm engine and the JS one are undefined so there is no + guaranty that the opfsVfs instance would be available in one + environment or the other when sqlite3_os_end() is called (_if_ it + gets called at all in a wasm build, which is undefined). + */ + /** + State which we send to the async-api Worker or share with it. + This object must initially contain only cloneable or sharable + objects. After the worker's "inited" message arrives, other types + of data may be added to it. + + For purposes of Atomics.wait() and Atomics.notify(), we use a + SharedArrayBuffer with one slot reserved for each of the API + proxy's methods. The sync side of the API uses Atomics.wait() + on the corresponding slot and the async side uses + Atomics.notify() on that slot. + + The approach of using a single SAB to serialize comms for all + instances might(?) lead to deadlock situations in multi-db + cases. We should probably have one SAB here with a single slot + for locking a per-file initialization step and then allocate a + separate SAB like the above one for each file. That will + require a bit of acrobatics but should be feasible. The most + problematic part is that xOpen() would have to use + postMessage() to communicate its SharedArrayBuffer, and mixing + that approach with Atomics.wait/notify() gets a bit messy. + */ + const state = Object.create(null); + state.verbose = options.verbose; + state.littleEndian = (() => { + const buffer = /* @__PURE__ */ new ArrayBuffer(2); + new DataView(buffer).setInt16(0, 256, true); + return new Int16Array(buffer)[0] === 256; + })(); + /** + asyncIdleWaitTime is how long (ms) to wait, in the async proxy, + for each Atomics.wait() when waiting on inbound VFS API calls. + We need to wake up periodically to give the thread a chance to + do other things. If this is too high (e.g. 500ms) then even two + workers/tabs can easily run into locking errors. Some multiple + of this value is also used for determining how long to wait on + lock contention to free up. + */ + state.asyncIdleWaitTime = 150; + /** + Whether the async counterpart should log exceptions to + the serialization channel. That produces a great deal of + noise for seemingly innocuous things like xAccess() checks + for missing files, so this option may have one of 3 values: + + 0 = no exception logging. + + 1 = only log exceptions for "significant" ops like xOpen(), + xRead(), and xWrite(). + + 2 = log all exceptions. + */ + state.asyncS11nExceptions = 1; + state.fileBufferSize = 1024 * 64; + state.sabS11nOffset = state.fileBufferSize; + /** + The size of the block in our SAB for serializing arguments and + result values. Needs to be large enough to hold serialized + values of any of the proxied APIs. Filenames are the largest + part but are limited to opfsVfs.$mxPathname bytes. We also + store exceptions there, so it needs to be long enough to hold + a reasonably long exception string. + */ + state.sabS11nSize = opfsVfs.$mxPathname * 2; + /** + The SAB used for all data I/O between the synchronous and + async halves (file i/o and arg/result s11n). + */ + state.sabIO = new SharedArrayBuffer(state.fileBufferSize + state.sabS11nSize); + state.opIds = Object.create(null); + const metrics = Object.create(null); + { + let i = 0; + state.opIds.whichOp = i++; + state.opIds.rc = i++; + state.opIds.xAccess = i++; + state.opIds.xClose = i++; + state.opIds.xDelete = i++; + state.opIds.xDeleteNoWait = i++; + state.opIds.xFileSize = i++; + state.opIds.xLock = i++; + state.opIds.xOpen = i++; + state.opIds.xRead = i++; + state.opIds.xSleep = i++; + state.opIds.xSync = i++; + state.opIds.xTruncate = i++; + state.opIds.xUnlock = i++; + state.opIds.xWrite = i++; + state.opIds.mkdir = i++; + state.opIds["opfs-async-metrics"] = i++; + state.opIds["opfs-async-shutdown"] = i++; + state.opIds.retry = i++; + state.sabOP = new SharedArrayBuffer(i * 4); + opfsUtil.metrics.reset(); + } + /** + SQLITE_xxx constants to export to the async worker + counterpart... + */ + state.sq3Codes = Object.create(null); + [ + "SQLITE_ACCESS_EXISTS", + "SQLITE_ACCESS_READWRITE", + "SQLITE_BUSY", + "SQLITE_CANTOPEN", + "SQLITE_ERROR", + "SQLITE_IOERR", + "SQLITE_IOERR_ACCESS", + "SQLITE_IOERR_CLOSE", + "SQLITE_IOERR_DELETE", + "SQLITE_IOERR_FSYNC", + "SQLITE_IOERR_LOCK", + "SQLITE_IOERR_READ", + "SQLITE_IOERR_SHORT_READ", + "SQLITE_IOERR_TRUNCATE", + "SQLITE_IOERR_UNLOCK", + "SQLITE_IOERR_WRITE", + "SQLITE_LOCK_EXCLUSIVE", + "SQLITE_LOCK_NONE", + "SQLITE_LOCK_PENDING", + "SQLITE_LOCK_RESERVED", + "SQLITE_LOCK_SHARED", + "SQLITE_LOCKED", + "SQLITE_MISUSE", + "SQLITE_NOTFOUND", + "SQLITE_OPEN_CREATE", + "SQLITE_OPEN_DELETEONCLOSE", + "SQLITE_OPEN_MAIN_DB", + "SQLITE_OPEN_READONLY" + ].forEach((k) => { + if (void 0 === (state.sq3Codes[k] = capi[k])) toss("Maintenance required: not found:", k); + }); + state.opfsFlags = Object.assign(Object.create(null), { + OPFS_UNLOCK_ASAP: 1, + OPFS_UNLINK_BEFORE_OPEN: 2, + defaultUnlockAsap: false + }); + /** + Runs the given operation (by name) in the async worker + counterpart, waits for its response, and returns the result + which the async worker writes to SAB[state.opIds.rc]. The + 2nd and subsequent arguments must be the arguments for the + async op. + */ + const opRun = (op, ...args) => { + const opNdx = state.opIds[op] || toss("Invalid op ID:", op); + state.s11n.serialize(...args); + Atomics.store(state.sabOPView, state.opIds.rc, -1); + Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); + Atomics.notify(state.sabOPView, state.opIds.whichOp); + const t = performance.now(); + while ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.rc, -1)); + const rc = Atomics.load(state.sabOPView, state.opIds.rc); + metrics[op].wait += performance.now() - t; + if (rc && state.asyncS11nExceptions) { + const err = state.s11n.deserialize(); + if (err) error(op + "() async error:", ...err); + } + return rc; + }; + /** + Not part of the public API. Only for test/development use. + */ + opfsUtil.debug = { + asyncShutdown: () => { + warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); + opRun("opfs-async-shutdown"); + }, + asyncRestart: () => { + warn("Attempting to restart OPFS VFS async listener. Might work, might not."); + W.postMessage({ type: "opfs-async-restart" }); + } + }; + const initS11n = () => { + /** + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ACHTUNG: this code is 100% duplicated in the other half of + this proxy! The documentation is maintained in the + "synchronous half". + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + This proxy de/serializes cross-thread function arguments and + output-pointer values via the state.sabIO SharedArrayBuffer, + using the region defined by (state.sabS11nOffset, + state.sabS11nOffset + state.sabS11nSize]. Only one dataset is + recorded at a time. + + This is not a general-purpose format. It only supports the + range of operations, and data sizes, needed by the + sqlite3_vfs and sqlite3_io_methods operations. Serialized + data are transient and this serialization algorithm may + change at any time. + + The data format can be succinctly summarized as: + + Nt...Td...D + + Where: + + - N = number of entries (1 byte) + + - t = type ID of first argument (1 byte) + + - ...T = type IDs of the 2nd and subsequent arguments (1 byte + each). + + - d = raw bytes of first argument (per-type size). + + - ...D = raw bytes of the 2nd and subsequent arguments (per-type + size). + + All types except strings have fixed sizes. Strings are stored + using their TextEncoder/TextDecoder representations. It would + arguably make more sense to store them as Int16Arrays of + their JS character values, but how best/fastest to get that + in and out of string form is an open point. Initial + experimentation with that approach did not gain us any speed. + + Historical note: this impl was initially about 1% this size by + using using JSON.stringify/parse(), but using fit-to-purpose + serialization saves considerable runtime. + */ + if (state.s11n) return state.s11n; + const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + state.s11n = Object.create(null); + const TypeIds = Object.create(null); + TypeIds.number = { + id: 1, + size: 8, + getter: "getFloat64", + setter: "setFloat64" + }; + TypeIds.bigint = { + id: 2, + size: 8, + getter: "getBigInt64", + setter: "setBigInt64" + }; + TypeIds.boolean = { + id: 3, + size: 4, + getter: "getInt32", + setter: "setInt32" + }; + TypeIds.string = { id: 4 }; + const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v); + const getTypeIdById = (tid) => { + switch (tid) { + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:", tid); + } + }; + /** + Returns an array of the deserialized state stored by the most + recent serialize() operation (from this thread or the + counterpart thread), or null if the serialization buffer is + empty. If passed a truthy argument, the serialization buffer + is cleared after deserialization. + */ + state.s11n.deserialize = function(clear = false) { + ++metrics.s11n.deserialize.count; + const t = performance.now(); + const argc = viewU8[0]; + const rc = argc ? [] : null; + if (argc) { + const typeIds = []; + let offset = 1, i, n, v; + for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset])); + for (i = 0; i < argc; ++i) { + const t = typeIds[i]; + if (t.getter) { + v = viewDV[t.getter](offset, state.littleEndian); + offset += t.size; + } else { + n = viewDV.getInt32(offset, state.littleEndian); + offset += 4; + v = textDecoder.decode(viewU8.slice(offset, offset + n)); + offset += n; + } + rc.push(v); + } + } + if (clear) viewU8[0] = 0; + metrics.s11n.deserialize.time += performance.now() - t; + return rc; + }; + /** + Serializes all arguments to the shared buffer for consumption + by the counterpart thread. + + This routine is only intended for serializing OPFS VFS + arguments and (in at least one special case) result values, + and the buffer is sized to be able to comfortably handle + those. + + If passed no arguments then it zeroes out the serialization + state. + */ + state.s11n.serialize = function(...args) { + const t = performance.now(); + ++metrics.s11n.serialize.count; + if (args.length) { + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 255; + for (; i < args.length; ++i, ++offset) { + typeIds.push(getTypeId(args[i])); + viewU8[offset] = typeIds[i].id; + } + for (i = 0; i < args.length; ++i) { + const t = typeIds[i]; + if (t.setter) { + viewDV[t.setter](offset, args[i], state.littleEndian); + offset += t.size; + } else { + const s = textEncoder.encode(args[i]); + viewDV.setInt32(offset, s.byteLength, state.littleEndian); + offset += 4; + viewU8.set(s, offset); + offset += s.byteLength; + } + } + } else viewU8[0] = 0; + metrics.s11n.serialize.time += performance.now() - t; + }; + return state.s11n; + }; + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + const randomFilename = function f(len = 16) { + if (!f._chars) { + f._chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for (; i < len; ++i) { + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(""); + }; + /** + Map of sqlite3_file pointers to objects constructed by xOpen(). + */ + const __openFiles = Object.create(null); + const opTimer = Object.create(null); + opTimer.op = void 0; + opTimer.start = void 0; + const mTimeStart = (op) => { + opTimer.start = performance.now(); + opTimer.op = op; + ++metrics[op].count; + }; + const mTimeEnd = () => metrics[opTimer.op].time += performance.now() - opTimer.start; + /** + Impls for the sqlite3_io_methods methods. Maintenance reminder: + members are in alphabetical order to simplify finding them. + */ + const ioSyncWrappers = { + xCheckReservedLock: function(pFile, pOut) { + /** + As of late 2022, only a single lock can be held on an OPFS + file. We have no way of checking whether any _other_ db + connection has a lock except by trying to obtain and (on + success) release a sync-handle for it, but doing so would + involve an inherent race condition. For the time being, + pending a better solution, we simply report whether the + given pFile is open. + + Update 2024-06-12: based on forum discussions, this + function now always sets pOut to 0 (false): + + https://sqlite.org/forum/forumpost/a2f573b00cda1372 + */ + wasm.poke(pOut, 0, "i32"); + return 0; + }, + xClose: function(pFile) { + mTimeStart("xClose"); + let rc = 0; + const f = __openFiles[pFile]; + if (f) { + delete __openFiles[pFile]; + rc = opRun("xClose", pFile); + if (f.sq3File) f.sq3File.dispose(); + } + mTimeEnd(); + return rc; + }, + xDeviceCharacteristics: function(pFile) { + return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + }, + xFileControl: function(pFile, opId, pArg) { + return capi.SQLITE_NOTFOUND; + }, + xFileSize: function(pFile, pSz64) { + mTimeStart("xFileSize"); + let rc = opRun("xFileSize", pFile); + if (0 == rc) try { + const sz = state.s11n.deserialize()[0]; + wasm.poke(pSz64, sz, "i64"); + } catch (e) { + error("Unexpected error reading xFileSize() result:", e); + rc = state.sq3Codes.SQLITE_IOERR; + } + mTimeEnd(); + return rc; + }, + xLock: function(pFile, lockType) { + mTimeStart("xLock"); + const f = __openFiles[pFile]; + let rc = 0; + if (!f.lockType) { + rc = opRun("xLock", pFile, lockType); + if (0 === rc) f.lockType = lockType; + } else f.lockType = lockType; + mTimeEnd(); + return rc; + }, + xRead: function(pFile, pDest, n, offset64) { + mTimeStart("xRead"); + const f = __openFiles[pFile]; + let rc; + try { + rc = opRun("xRead", pFile, n, Number(offset64)); + if (0 === rc || capi.SQLITE_IOERR_SHORT_READ === rc) + /** + Results get written to the SharedArrayBuffer f.sabView. + Because the heap is _not_ a SharedArrayBuffer, we have + to copy the results. TypedArray.set() seems to be the + fastest way to copy this. */ + wasm.heap8u().set(f.sabView.subarray(0, n), Number(pDest)); + } catch (e) { + error("xRead(", arguments, ") failed:", e, f); + rc = capi.SQLITE_IOERR_READ; + } + mTimeEnd(); + return rc; + }, + xSync: function(pFile, flags) { + mTimeStart("xSync"); + ++metrics.xSync.count; + const rc = opRun("xSync", pFile, flags); + mTimeEnd(); + return rc; + }, + xTruncate: function(pFile, sz64) { + mTimeStart("xTruncate"); + const rc = opRun("xTruncate", pFile, Number(sz64)); + mTimeEnd(); + return rc; + }, + xUnlock: function(pFile, lockType) { + mTimeStart("xUnlock"); + const f = __openFiles[pFile]; + let rc = 0; + if (capi.SQLITE_LOCK_NONE === lockType && f.lockType) rc = opRun("xUnlock", pFile, lockType); + if (0 === rc) f.lockType = lockType; + mTimeEnd(); + return rc; + }, + xWrite: function(pFile, pSrc, n, offset64) { + mTimeStart("xWrite"); + const f = __openFiles[pFile]; + let rc; + try { + f.sabView.set(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n)); + rc = opRun("xWrite", pFile, n, Number(offset64)); + } catch (e) { + error("xWrite(", arguments, ") failed:", e, f); + rc = capi.SQLITE_IOERR_WRITE; + } + mTimeEnd(); + return rc; + } + }; + /** + Impls for the sqlite3_vfs methods. Maintenance reminder: members + are in alphabetical order to simplify finding them. + */ + const vfsSyncWrappers = { + xAccess: function(pVfs, zName, flags, pOut) { + mTimeStart("xAccess"); + const rc = opRun("xAccess", wasm.cstrToJs(zName)); + wasm.poke(pOut, rc ? 0 : 1, "i32"); + mTimeEnd(); + return 0; + }, + xCurrentTime: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); + return 0; + }, + xCurrentTimeInt64: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); + return 0; + }, + xDelete: function(pVfs, zName, doSyncDir) { + mTimeStart("xDelete"); + const rc = opRun("xDelete", wasm.cstrToJs(zName), doSyncDir, false); + mTimeEnd(); + return rc; + }, + xFullPathname: function(pVfs, zName, nOut, pOut) { + return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; + }, + xGetLastError: function(pVfs, nOut, pOut) { + warn("OPFS xGetLastError() has nothing sensible to return."); + return 0; + }, + xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { + mTimeStart("xOpen"); + let opfsFlags = 0; + if (0 === zName) zName = randomFilename(); + else if (wasm.isPtr(zName)) { + if (capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP; + if (capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; + zName = wasm.cstrToJs(zName); + } + const fh = Object.create(null); + fh.fid = pFile; + fh.filename = zName; + fh.sab = new SharedArrayBuffer(state.fileBufferSize); + fh.flags = flags; + fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) && !!(flags & capi.SQLITE_OPEN_READONLY); + const rc = opRun("xOpen", pFile, zName, flags, opfsFlags); + if (!rc) { + if (fh.readOnly) wasm.poke(pOutFlags, capi.SQLITE_OPEN_READONLY, "i32"); + __openFiles[pFile] = fh; + fh.sabView = state.sabFileBufView; + fh.sq3File = new sqlite3_file(pFile); + fh.sq3File.$pMethods = opfsIoMethods.pointer; + fh.lockType = capi.SQLITE_LOCK_NONE; + } + mTimeEnd(); + return rc; + } + }; + if (dVfs) { + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + } + if (!opfsVfs.$xRandomness) vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut) { + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; + return i; + }; + if (!opfsVfs.$xSleep) vfsSyncWrappers.xSleep = function(pVfs, ms) { + Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); + return 0; + }; + /** + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd arg + is true, the result is returned as an array of path elements, + else an absolute path string is returned. + */ + opfsUtil.getResolvedPath = function(filename, splitIt) { + const p = new URL(filename, "file://irrelevant").pathname; + return splitIt ? p.split("/").filter((v) => !!v) : p; + }; + /** + Takes the absolute path to a filesystem element. Returns an + array of [handleOfContainingDir, filename]. If the 2nd argument + is truthy then each directory element leading to the file is + created along the way. Throws if any creation or resolution + fails. + */ + opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false) { + const path = opfsUtil.getResolvedPath(absFilename, true); + const filename = path.pop(); + let dh = opfsUtil.rootDirectory; + for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); + return [dh, filename]; + }; + /** + Creates the given directory name, recursively, in + the OPFS filesystem. Returns true if it succeeds or the + directory already exists, else false. + */ + opfsUtil.mkdir = async function(absDirName) { + try { + await opfsUtil.getDirForFilename(absDirName + "/filepart", true); + return true; + } catch (e) { + return false; + } + }; + /** + Checks whether the given OPFS filesystem entry exists, + returning true if it does, false if it doesn't or if an + exception is intercepted while trying to make the + determination. + */ + opfsUtil.entryExists = async function(fsEntryName) { + try { + const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); + await dh.getFileHandle(fn); + return true; + } catch (e) { + return false; + } + }; + /** + Generates a random ASCII string, intended for use as a + temporary file name. Its argument is the length of the string, + defaulting to 16. + */ + opfsUtil.randomFilename = randomFilename; + /** + Returns a promise which resolves to an object which represents + all files and directories in the OPFS tree. The top-most object + has two properties: `dirs` is an array of directory entries + (described below) and `files` is a list of file names for all + files in that directory. + + Traversal starts at sqlite3.opfs.rootDirectory. + + Each `dirs` entry is an object in this form: + + ``` + { name: directoryName, + dirs: [...subdirs], + files: [...file names] + } + ``` + + The `files` and `subdirs` entries are always set but may be + empty arrays. + + The returned object has the same structure but its `name` is + an empty string. All returned objects are created with + Object.create(null), so have no prototype. + + Design note: the entries do not contain more information, + e.g. file sizes, because getting such info is not only + expensive but is subject to locking-related errors. + */ + opfsUtil.treeList = async function() { + const doDir = async function callee(dirHandle, tgt) { + tgt.name = dirHandle.name; + tgt.dirs = []; + tgt.files = []; + for await (const handle of dirHandle.values()) if ("directory" === handle.kind) { + const subDir = Object.create(null); + tgt.dirs.push(subDir); + await callee(handle, subDir); + } else tgt.files.push(handle.name); + }; + const root = Object.create(null); + await doDir(opfsUtil.rootDirectory, root); + return root; + }; + /** + Irrevocably deletes _all_ files in the current origin's OPFS. + Obviously, this must be used with great caution. It may throw + an exception if removal of anything fails (e.g. a file is + locked), but the precise conditions under which the underlying + APIs will throw are not documented (so we cannot tell you what + they are). + */ + opfsUtil.rmfr = async function() { + const dir = opfsUtil.rootDirectory, opt = { recurse: true }; + for await (const handle of dir.values()) dir.removeEntry(handle.name, opt); + }; + /** + Deletes the given OPFS filesystem entry. As this environment + has no notion of "current directory", the given name must be an + absolute path. If the 2nd argument is truthy, deletion is + recursive (use with caution!). + + The returned Promise resolves to true if the deletion was + successful, else false (but...). The OPFS API reports the + reason for the failure only in human-readable form, not + exceptions which can be type-checked to determine the + failure. Because of that... + + If the final argument is truthy then this function will + propagate any exception on error, rather than returning false. + */ + opfsUtil.unlink = async function(fsEntryName, recursive = false, throwOnError = false) { + try { + const [hDir, filenamePart] = await opfsUtil.getDirForFilename(fsEntryName, false); + await hDir.removeEntry(filenamePart, { recursive }); + return true; + } catch (e) { + if (throwOnError) throw new Error("unlink(", arguments[0], ") failed: " + e.message, { cause: e }); + return false; + } + }; + /** + Traverses the OPFS filesystem, calling a callback for each + entry. The argument may be either a callback function or an + options object with any of the following properties: + + - `callback`: function which gets called for each filesystem + entry. It gets passed 3 arguments: 1) the + FileSystemFileHandle or FileSystemDirectoryHandle of each + entry (noting that both are instanceof FileSystemHandle). 2) + the FileSystemDirectoryHandle of the parent directory. 3) the + current depth level, with 0 being at the top of the tree + relative to the starting directory. If the callback returns a + literal false, as opposed to any other falsy value, traversal + stops without an error. Any exceptions it throws are + propagated. Results are undefined if the callback manipulate + the filesystem (e.g. removing or adding entries) because the + how OPFS iterators behave in the face of such changes is + undocumented. + + - `recursive` [bool=true]: specifies whether to recurse into + subdirectories or not. Whether recursion is depth-first or + breadth-first is unspecified! + + - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] + specifies the starting directory. + + If this function is passed a function, it is assumed to be the + callback. + + Returns a promise because it has to (by virtue of being async) + but that promise has no specific meaning: the traversal it + performs is synchronous. The promise must be used to catch any + exceptions propagated by the callback, however. + */ + opfsUtil.traverse = async function(opt) { + const defaultOpt = { + recursive: true, + directory: opfsUtil.rootDirectory + }; + if ("function" === typeof opt) opt = { callback: opt }; + opt = Object.assign(defaultOpt, opt || {}); + (async function callee(dirHandle, depth) { + for await (const handle of dirHandle.values()) if (false === opt.callback(handle, dirHandle, depth)) return false; + else if (opt.recursive && "directory" === handle.kind) { + if (false === await callee(handle, depth + 1)) break; + } + })(opt.directory, 0); + }; + /** + impl of importDb() when it's given a function as its second + argument. + */ + const importDbChunked = async function(filename, callback) { + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + let sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(), nWrote = 0, chunk, checkedHeader = false; + try { + sah.truncate(0); + while (void 0 !== (chunk = await callback())) { + if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, { at: nWrote }); + nWrote += chunk.byteLength; + } + if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); + if (!checkedHeader) { + const header = new Uint8Array(20); + sah.read(header, { at: 0 }); + util.affirmDbHeader(header); + } + sah.write(new Uint8Array([1, 1]), { at: 18 }); + return nWrote; + } catch (e) { + await sah.close(); + sah = void 0; + await hDir.removeEntry(fnamePart).catch(() => {}); + throw e; + } finally { + if (sah) await sah.close(); + } + }; + /** + Asynchronously imports the given bytes (a byte array or + ArrayBuffer) into the given database file. + + Results are undefined if the given db name refers to an opened + db. + + If passed a function for its second argument, its behaviour + changes: imports its data in chunks fed to it by the given + callback function. It calls the callback (which may be async) + repeatedly, expecting either a Uint8Array or ArrayBuffer (to + denote new input) or undefined (to denote EOF). For so long as + the callback continues to return non-undefined, it will append + incoming data to the given VFS-hosted database file. When + called this way, the resolved value of the returned Promise is + the number of bytes written to the target file. + + It very specifically requires the input to be an SQLite3 + database and throws if that's not the case. It does so in + order to prevent this function from taking on a larger scope + than it is specifically intended to. i.e. we do not want it to + become a convenience for importing arbitrary files into OPFS. + + This routine rewrites the database header bytes in the output + file (not the input array) to force disabling of WAL mode. + + On error this throws and the state of the input file is + undefined (it depends on where the exception was triggered). + + On success, resolves to the number of bytes written. + */ + opfsUtil.importDb = async function(filename, bytes) { + if (bytes instanceof Function) return importDbChunked(filename, bytes); + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + util.affirmIsDb(bytes); + const n = bytes.byteLength; + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + let sah, nWrote = 0; + try { + sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(); + sah.truncate(0); + nWrote = sah.write(bytes, { at: 0 }); + if (nWrote != n) toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); + sah.write(new Uint8Array([1, 1]), { at: 18 }); + return nWrote; + } catch (e) { + if (sah) { + await sah.close(); + sah = void 0; + } + await hDir.removeEntry(fnamePart).catch(() => {}); + throw e; + } finally { + if (sah) await sah.close(); + } + }; + if (sqlite3.oo1) { + const OpfsDb = function(...args) { + const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); + opt.vfs = opfsVfs.$zName; + sqlite3.oo1.DB.dbCtorHelper.call(this, opt); + }; + OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); + sqlite3.oo1.OpfsDb = OpfsDb; + OpfsDb.importDb = opfsUtil.importDb; + sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback(opfsVfs.pointer, function(oo1Db, sqlite3) { + sqlite3.capi.sqlite3_busy_timeout(oo1Db, 1e4); + }); + } + const sanityCheck = function() { + const scope = wasm.scopedAllocPush(); + const sq3File = new sqlite3_file(); + try { + const fid = sq3File.pointer; + const openFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE | capi.SQLITE_OPEN_MAIN_DB; + const pOut = wasm.scopedAlloc(8); + const dbFile = "/sanity/check/file" + randomFilename(8); + const zDbFile = wasm.scopedAllocCString(dbFile); + let rc; + state.s11n.serialize("This is ä string."); + rc = state.s11n.deserialize(); + log("deserialize() says:", rc); + if ("This is ä string." !== rc[0]) toss("String d13n error."); + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut, "i32"); + log("xAccess(", dbFile, ") exists ?=", rc); + rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, fid, openFlags, pOut); + log("open rc =", rc, "state.sabOPView[xOpen] =", state.sabOPView[state.opIds.xOpen]); + if (0 !== rc) { + error("open failed with code", rc); + return; + } + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut, "i32"); + if (!rc) toss("xAccess() failed to detect file."); + rc = ioSyncWrappers.xSync(sq3File.pointer, 0); + if (rc) toss("sync failed w/ rc", rc); + rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); + if (rc) toss("truncate failed w/ rc", rc); + wasm.poke(pOut, 0, "i64"); + rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); + if (rc) toss("xFileSize failed w/ rc", rc); + log("xFileSize says:", wasm.peek(pOut, "i64")); + rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); + if (rc) toss("xWrite() failed!"); + const readBuf = wasm.scopedAlloc(16); + rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); + wasm.poke(readBuf + 6, 0); + let jRead = wasm.cstrToJs(readBuf); + log("xRead() got:", jRead); + if ("sanity" !== jRead) toss("Unexpected xRead() value."); + if (vfsSyncWrappers.xSleep) { + log("xSleep()ing before close()ing..."); + vfsSyncWrappers.xSleep(opfsVfs.pointer, 2e3); + log("waking up from xSleep()"); + } + rc = ioSyncWrappers.xClose(fid); + log("xClose rc =", rc, "sabOPView =", state.sabOPView); + log("Deleting file:", dbFile); + vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 4660); + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut, "i32"); + if (rc) toss("Expecting 0 from xAccess(", dbFile, ") after xDelete()."); + warn("End of OPFS sanity checks."); + } finally { + sq3File.dispose(); + wasm.scopedAllocPop(scope); + } + }; + W.onmessage = function({ data }) { + switch (data.type) { + case "opfs-unavailable": + promiseReject(new Error(data.payload.join(" "))); + break; + case "opfs-async-loaded": + W.postMessage({ + type: "opfs-async-init", + args: state + }); + break; + case "opfs-async-inited": + if (true === promiseWasRejected) break; + try { + sqlite3.vfs.installVfs({ + io: { + struct: opfsIoMethods, + methods: ioSyncWrappers + }, + vfs: { + struct: opfsVfs, + methods: vfsSyncWrappers + } + }); + state.sabOPView = new Int32Array(state.sabOP); + state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); + state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + initS11n(); + if (options.sanityChecks) { + warn("Running sanity checks because of opfs-sanity-check URL arg..."); + sanityCheck(); + } + if (thisThreadHasOPFS()) navigator.storage.getDirectory().then((d) => { + W.onerror = W._originalOnError; + delete W._originalOnError; + sqlite3.opfs = opfsUtil; + opfsUtil.rootDirectory = d; + log("End of OPFS sqlite3_vfs setup.", opfsVfs); + promiseResolve(); + }).catch(promiseReject); + else promiseResolve(); + } catch (e) { + error(e); + promiseReject(e); + } + break; + default: { + const errMsg = "Unexpected message from the OPFS async worker: " + JSON.stringify(data); + error(errMsg); + promiseReject(new Error(errMsg)); + break; + } + } + }; + }); + }; + installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js"; + globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3) => { + try { + let proxyJs = installOpfsVfs.defaultProxyUri; + if (sqlite3?.scriptInfo?.sqlite3Dir) installOpfsVfs.defaultProxyUri = sqlite3.scriptInfo.sqlite3Dir + proxyJs; + return installOpfsVfs().catch((e) => { + sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:", e.message); + }); + } catch (e) { + sqlite3.config.error("installOpfsVfs() exception:", e); + return Promise.reject(e); + } + }); + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + "use strict"; + const toss = sqlite3.util.toss; + const toss3 = sqlite3.util.toss3; + const initPromises = Object.create(null); + const capi = sqlite3.capi; + const util = sqlite3.util; + const wasm = sqlite3.wasm; + const SECTOR_SIZE = 4096; + const HEADER_MAX_PATH_SIZE = 512; + const HEADER_FLAGS_SIZE = 4; + const HEADER_DIGEST_SIZE = 8; + const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE; + const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE; + const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE; + const HEADER_OFFSET_DATA = SECTOR_SIZE; + const PERSISTENT_FILE_TYPES = capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_MAIN_JOURNAL | capi.SQLITE_OPEN_SUPER_JOURNAL | capi.SQLITE_OPEN_WAL; + const FLAG_COMPUTE_DIGEST_V2 = capi.SQLITE_OPEN_MEMORY; + /** Subdirectory of the VFS's space where "opaque" (randomly-named) + files are stored. Changing this effectively invalidates the data + stored under older names (orphaning it), so don't do that. */ + const OPAQUE_DIR_NAME = ".opaque"; + /** + Returns short a string of random alphanumeric characters + suitable for use as a random filename. + */ + const getRandomName = () => Math.random().toString(36).slice(2); + const textDecoder = new TextDecoder(); + const textEncoder = new TextEncoder(); + const optionDefaults = Object.assign(Object.create(null), { + name: "opfs-sahpool", + directory: void 0, + initialCapacity: 6, + clearOnInit: false, + verbosity: 2, + forceReinitIfPreviouslyFailed: false + }); + /** Logging routines, from most to least serious. */ + const loggers = [ + sqlite3.config.error, + sqlite3.config.warn, + sqlite3.config.log + ]; + sqlite3.config.log; + const warn = sqlite3.config.warn; + sqlite3.config.error; + const __mapVfsToPool = /* @__PURE__ */ new Map(); + const getPoolForVfs = (pVfs) => __mapVfsToPool.get(pVfs); + const setPoolForVfs = (pVfs, pool) => { + if (pool) __mapVfsToPool.set(pVfs, pool); + else __mapVfsToPool.delete(pVfs); + }; + const __mapSqlite3File = /* @__PURE__ */ new Map(); + const getPoolForPFile = (pFile) => __mapSqlite3File.get(pFile); + const setPoolForPFile = (pFile, pool) => { + if (pool) __mapSqlite3File.set(pFile, pool); + else __mapSqlite3File.delete(pFile); + }; + /** + Impls for the sqlite3_io_methods methods. Maintenance reminder: + members are in alphabetical order to simplify finding them. + */ + const ioMethods = { + xCheckReservedLock: function(pFile, pOut) { + const pool = getPoolForPFile(pFile); + pool.log("xCheckReservedLock"); + pool.storeErr(); + wasm.poke32(pOut, 1); + return 0; + }, + xClose: function(pFile) { + const pool = getPoolForPFile(pFile); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + if (file) try { + pool.log(`xClose ${file.path}`); + pool.mapS3FileToOFile(pFile, false); + file.sah.flush(); + if (file.flags & capi.SQLITE_OPEN_DELETEONCLOSE) pool.deletePath(file.path); + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + return 0; + }, + xDeviceCharacteristics: function(pFile) { + return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + }, + xFileControl: function(pFile, opId, pArg) { + return capi.SQLITE_NOTFOUND; + }, + xFileSize: function(pFile, pSz64) { + const pool = getPoolForPFile(pFile); + pool.log(`xFileSize`); + const size = pool.getOFileForS3File(pFile).sah.getSize() - HEADER_OFFSET_DATA; + wasm.poke64(pSz64, BigInt(size)); + return 0; + }, + xLock: function(pFile, lockType) { + const pool = getPoolForPFile(pFile); + pool.log(`xLock ${lockType}`); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + file.lockType = lockType; + return 0; + }, + xRead: function(pFile, pDest, n, offset64) { + const pool = getPoolForPFile(pFile); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + pool.log(`xRead ${file.path} ${n} @ ${offset64}`); + try { + const nRead = file.sah.read(wasm.heap8u().subarray(Number(pDest), Number(pDest) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }); + if (nRead < n) { + wasm.heap8u().fill(0, Number(pDest) + nRead, Number(pDest) + n); + return capi.SQLITE_IOERR_SHORT_READ; + } + return 0; + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + }, + xSectorSize: function(pFile) { + return SECTOR_SIZE; + }, + xSync: function(pFile, flags) { + const pool = getPoolForPFile(pFile); + pool.log(`xSync ${flags}`); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + try { + file.sah.flush(); + return 0; + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + }, + xTruncate: function(pFile, sz64) { + const pool = getPoolForPFile(pFile); + pool.log(`xTruncate ${sz64}`); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + try { + file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64)); + return 0; + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + }, + xUnlock: function(pFile, lockType) { + const pool = getPoolForPFile(pFile); + pool.log("xUnlock"); + const file = pool.getOFileForS3File(pFile); + file.lockType = lockType; + return 0; + }, + xWrite: function(pFile, pSrc, n, offset64) { + const pool = getPoolForPFile(pFile); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + pool.log(`xWrite ${file.path} ${n} ${offset64}`); + try { + return n === file.sah.write(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }) ? 0 : toss("Unknown write() failure."); + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + } + }; + const opfsIoMethods = new capi.sqlite3_io_methods(); + opfsIoMethods.$iVersion = 1; + sqlite3.vfs.installVfs({ io: { + struct: opfsIoMethods, + methods: ioMethods + } }); + /** + Impls for the sqlite3_vfs methods. Maintenance reminder: members + are in alphabetical order to simplify finding them. + */ + const vfsMethods = { + xAccess: function(pVfs, zName, flags, pOut) { + const pool = getPoolForVfs(pVfs); + pool.storeErr(); + try { + const name = pool.getPath(zName); + wasm.poke32(pOut, pool.hasFilename(name) ? 1 : 0); + } catch (e) { + wasm.poke32(pOut, 0); + } + return 0; + }, + xCurrentTime: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); + return 0; + }, + xCurrentTimeInt64: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); + return 0; + }, + xDelete: function(pVfs, zName, doSyncDir) { + const pool = getPoolForVfs(pVfs); + pool.log(`xDelete ${wasm.cstrToJs(zName)}`); + pool.storeErr(); + try { + pool.deletePath(pool.getPath(zName)); + return 0; + } catch (e) { + pool.storeErr(e); + return capi.SQLITE_IOERR_DELETE; + } + }, + xFullPathname: function(pVfs, zName, nOut, pOut) { + return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; + }, + xGetLastError: function(pVfs, nOut, pOut) { + const pool = getPoolForVfs(pVfs); + const e = pool.popErr(); + pool.log(`xGetLastError ${nOut} e =`, e); + if (e) { + const scope = wasm.scopedAllocPush(); + try { + const [cMsg, n] = wasm.scopedAllocCString(e.message, true); + wasm.cstrncpy(pOut, cMsg, nOut); + if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); + } catch (e) { + return capi.SQLITE_NOMEM; + } finally { + wasm.scopedAllocPop(scope); + } + } + return e ? e.sqlite3Rc || capi.SQLITE_IOERR : 0; + }, + xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { + const pool = getPoolForVfs(pVfs); + try { + flags &= ~FLAG_COMPUTE_DIGEST_V2; + pool.log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`); + const path = zName && wasm.peek8(zName) ? pool.getPath(zName) : getRandomName(); + let sah = pool.getSAHForPath(path); + if (!sah && flags & capi.SQLITE_OPEN_CREATE) if (pool.getFileCount() < pool.getCapacity()) { + sah = pool.nextAvailableSAH(); + pool.setAssociatedPath(sah, path, flags); + } else toss("SAH pool is full. Cannot create file", path); + if (!sah) toss("file not found:", path); + const file = { + path, + flags, + sah + }; + pool.mapS3FileToOFile(pFile, file); + file.lockType = capi.SQLITE_LOCK_NONE; + const sq3File = new capi.sqlite3_file(pFile); + sq3File.$pMethods = opfsIoMethods.pointer; + sq3File.dispose(); + wasm.poke32(pOutFlags, flags); + return 0; + } catch (e) { + pool.storeErr(e); + return capi.SQLITE_CANTOPEN; + } + } + }; + /** + Creates, initializes, and returns an sqlite3_vfs instance for an + OpfsSAHPool. The argument is the VFS's name (JS string). + + Throws if the VFS name is already registered or if something + goes terribly wrong via sqlite3.vfs.installVfs(). + + Maintenance reminder: the only detail about the returned object + which is specific to any given OpfsSAHPool instance is the $zName + member. All other state is identical. + */ + const createOpfsVfs = function(vfsName) { + if (sqlite3.capi.sqlite3_vfs_find(vfsName)) toss3("VFS name is already registered:", vfsName); + const opfsVfs = new capi.sqlite3_vfs(); + const pDVfs = capi.sqlite3_vfs_find(null); + const dVfs = pDVfs ? new capi.sqlite3_vfs(pDVfs) : null; + opfsVfs.$iVersion = 2; + opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE; + opfsVfs.addOnDispose(opfsVfs.$zName = wasm.allocCString(vfsName), () => setPoolForVfs(opfsVfs.pointer, 0)); + if (dVfs) { + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + dVfs.dispose(); + } + if (!opfsVfs.$xRandomness && !vfsMethods.xRandomness) vfsMethods.xRandomness = function(pVfs, nOut, pOut) { + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; + return i; + }; + if (!opfsVfs.$xSleep && !vfsMethods.xSleep) vfsMethods.xSleep = (pVfs, ms) => 0; + sqlite3.vfs.installVfs({ vfs: { + struct: opfsVfs, + methods: vfsMethods + } }); + return opfsVfs; + }; + /** + Class for managing OPFS-related state for the + OPFS SharedAccessHandle Pool sqlite3_vfs. + */ + class OpfsSAHPool { + vfsDir; + #dhVfsRoot; + #dhOpaque; + #dhVfsParent; + #mapSAHToName = /* @__PURE__ */ new Map(); + #mapFilenameToSAH = /* @__PURE__ */ new Map(); + #availableSAH = /* @__PURE__ */ new Set(); + #mapS3FileToOFile_ = /* @__PURE__ */ new Map(); + /** Buffer used by [sg]etAssociatedPath(). */ + #apBody = new Uint8Array(HEADER_CORPUS_SIZE); + #dvBody; + #cVfs; + #verbosity; + constructor(options = Object.create(null)) { + this.#verbosity = options.verbosity ?? optionDefaults.verbosity; + this.vfsName = options.name || optionDefaults.name; + this.#cVfs = createOpfsVfs(this.vfsName); + setPoolForVfs(this.#cVfs.pointer, this); + this.vfsDir = options.directory || "." + this.vfsName; + this.#dvBody = new DataView(this.#apBody.buffer, this.#apBody.byteOffset); + this.isReady = this.reset(!!(options.clearOnInit ?? optionDefaults.clearOnInit)).then(() => { + if (this.$error) throw this.$error; + return this.getCapacity() ? Promise.resolve(void 0) : this.addCapacity(options.initialCapacity || optionDefaults.initialCapacity); + }); + } + #logImpl(level, ...args) { + if (this.#verbosity > level) loggers[level](this.vfsName + ":", ...args); + } + log(...args) { + this.#logImpl(2, ...args); + } + warn(...args) { + this.#logImpl(1, ...args); + } + error(...args) { + this.#logImpl(0, ...args); + } + getVfs() { + return this.#cVfs; + } + getCapacity() { + return this.#mapSAHToName.size; + } + getFileCount() { + return this.#mapFilenameToSAH.size; + } + getFileNames() { + const rc = []; + for (const n of this.#mapFilenameToSAH.keys()) rc.push(n); + return rc; + } + /** + Adds n files to the pool's capacity. This change is + persistent across settings. Returns a Promise which resolves + to the new capacity. + */ + async addCapacity(n) { + for (let i = 0; i < n; ++i) { + const name = getRandomName(); + const ah = await (await this.#dhOpaque.getFileHandle(name, { create: true })).createSyncAccessHandle(); + this.#mapSAHToName.set(ah, name); + this.setAssociatedPath(ah, "", 0); + } + return this.getCapacity(); + } + /** + Reduce capacity by n, but can only reduce up to the limit + of currently-available SAHs. Returns a Promise which resolves + to the number of slots really removed. + */ + async reduceCapacity(n) { + let nRm = 0; + for (const ah of Array.from(this.#availableSAH)) { + if (nRm === n || this.getFileCount() === this.getCapacity()) break; + const name = this.#mapSAHToName.get(ah); + ah.close(); + await this.#dhOpaque.removeEntry(name); + this.#mapSAHToName.delete(ah); + this.#availableSAH.delete(ah); + ++nRm; + } + return nRm; + } + /** + Releases all currently-opened SAHs. The only legal operation + after this is acquireAccessHandles() or (if this is called from + pauseVfs()) either of isPaused() or unpauseVfs(). + */ + releaseAccessHandles() { + for (const ah of this.#mapSAHToName.keys()) ah.close(); + this.#mapSAHToName.clear(); + this.#mapFilenameToSAH.clear(); + this.#availableSAH.clear(); + } + /** + Opens all files under this.vfsDir/this.#dhOpaque and acquires a + SAH for each. Returns a Promise which resolves to no value but + completes once all SAHs are acquired. If acquiring an SAH + throws, this.$error will contain the corresponding Error + object. + + If it throws, it releases any SAHs which it may have + acquired before the exception was thrown, leaving the VFS in a + well-defined but unusable state. + + If clearFiles is true, the client-stored state of each file is + cleared when its handle is acquired, including its name, flags, + and any data stored after the metadata block. + */ + async acquireAccessHandles(clearFiles = false) { + const files = []; + for await (const [name, h] of this.#dhOpaque) if ("file" === h.kind) files.push([name, h]); + return Promise.all(files.map(async ([name, h]) => { + try { + const ah = await h.createSyncAccessHandle(); + this.#mapSAHToName.set(ah, name); + if (clearFiles) { + ah.truncate(HEADER_OFFSET_DATA); + this.setAssociatedPath(ah, "", 0); + } else { + const path = this.getAssociatedPath(ah); + if (path) this.#mapFilenameToSAH.set(path, ah); + else this.#availableSAH.add(ah); + } + } catch (e) { + this.storeErr(e); + this.releaseAccessHandles(); + throw e; + } + })); + } + /** + Given an SAH, returns the client-specified name of + that file by extracting it from the SAH's header. + + On error, it disassociates SAH from the pool and + returns an empty string. + */ + getAssociatedPath(sah) { + sah.read(this.#apBody, { at: 0 }); + const flags = this.#dvBody.getUint32(HEADER_OFFSET_FLAGS); + if (this.#apBody[0] && (flags & capi.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) { + warn(`Removing file with unexpected flags ${flags.toString(16)}`, this.#apBody); + this.setAssociatedPath(sah, "", 0); + return ""; + } + const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4); + sah.read(fileDigest, { at: HEADER_OFFSET_DIGEST }); + const compDigest = this.computeDigest(this.#apBody, flags); + if (fileDigest.every((v, i) => v === compDigest[i])) { + const pathBytes = this.#apBody.findIndex((v) => 0 === v); + if (0 === pathBytes) sah.truncate(HEADER_OFFSET_DATA); + return pathBytes ? textDecoder.decode(this.#apBody.subarray(0, pathBytes)) : ""; + } else { + warn("Disassociating file with bad digest."); + this.setAssociatedPath(sah, "", 0); + return ""; + } + } + /** + Stores the given client-defined path and SQLITE_OPEN_xyz flags + into the given SAH. If path is an empty string then the file is + disassociated from the pool but its previous name is preserved + in the metadata. + */ + setAssociatedPath(sah, path, flags) { + const enc = textEncoder.encodeInto(path, this.#apBody); + if (HEADER_MAX_PATH_SIZE <= enc.written + 1) toss("Path too long:", path); + if (path && flags) flags |= FLAG_COMPUTE_DIGEST_V2; + this.#apBody.fill(0, enc.written, HEADER_MAX_PATH_SIZE); + this.#dvBody.setUint32(HEADER_OFFSET_FLAGS, flags); + const digest = this.computeDigest(this.#apBody, flags); + sah.write(this.#apBody, { at: 0 }); + sah.write(digest, { at: HEADER_OFFSET_DIGEST }); + sah.flush(); + if (path) { + this.#mapFilenameToSAH.set(path, sah); + this.#availableSAH.delete(sah); + } else { + sah.truncate(HEADER_OFFSET_DATA); + this.#availableSAH.add(sah); + } + } + /** + Computes a digest for the given byte array and returns it as a + two-element Uint32Array. This digest gets stored in the + metadata for each file as a validation check. Changing this + algorithm invalidates all existing databases for this VFS, so + don't do that. + + See the docs for FLAG_COMPUTE_DIGEST_V2 for more details. + */ + computeDigest(byteArray, fileFlags) { + if (fileFlags & FLAG_COMPUTE_DIGEST_V2) { + let h1 = 3735928559; + let h2 = 1103547991; + for (const v of byteArray) { + h1 = Math.imul(h1 ^ v, 2654435761); + h2 = Math.imul(h2 ^ v, 104729); + } + return new Uint32Array([h1 >>> 0, h2 >>> 0]); + } else return new Uint32Array([0, 0]); + } + /** + Re-initializes the state of the SAH pool, releasing and + re-acquiring all handles. + + See acquireAccessHandles() for the specifics of the clearFiles + argument. + */ + async reset(clearFiles) { + await this.isReady; + let h = await navigator.storage.getDirectory(), prev; + for (const d of this.vfsDir.split("/")) if (d) { + prev = h; + h = await h.getDirectoryHandle(d, { create: true }); + } + this.#dhVfsRoot = h; + this.#dhVfsParent = prev; + this.#dhOpaque = await this.#dhVfsRoot.getDirectoryHandle(OPAQUE_DIR_NAME, { create: true }); + this.releaseAccessHandles(); + return this.acquireAccessHandles(clearFiles); + } + /** + Returns the pathname part of the given argument, + which may be any of: + + - a URL object + - A JS string representing a file name + - Wasm C-string representing a file name + + All "../" parts and duplicate slashes are resolve/removed from + the returned result. + */ + getPath(arg) { + if (wasm.isPtr(arg)) arg = wasm.cstrToJs(arg); + return (arg instanceof URL ? arg : new URL(arg, "file://localhost/")).pathname; + } + /** + Removes the association of the given client-specified file + name (JS string) from the pool. Returns true if a mapping + is found, else false. + */ + deletePath(path) { + const sah = this.#mapFilenameToSAH.get(path); + if (sah) { + this.#mapFilenameToSAH.delete(path); + this.setAssociatedPath(sah, "", 0); + } + return !!sah; + } + /** + Sets e (an Error object) as this object's current error. Pass a + falsy (or no) value to clear it. If code is truthy it is + assumed to be an SQLITE_xxx result code, defaulting to + SQLITE_IOERR if code is falsy. + + Returns the 2nd argument. + */ + storeErr(e, code) { + if (e) { + e.sqlite3Rc = code || capi.SQLITE_IOERR; + this.error(e); + } + this.$error = e; + return code; + } + /** + Pops this object's Error object and returns + it (a falsy value if no error is set). + */ + popErr() { + const rc = this.$error; + this.$error = void 0; + return rc; + } + /** + Returns the next available SAH without removing + it from the set. + */ + nextAvailableSAH() { + const [rc] = this.#availableSAH.keys(); + return rc; + } + /** + Given an (sqlite3_file*), returns the mapped + xOpen file object. + */ + getOFileForS3File(pFile) { + return this.#mapS3FileToOFile_.get(pFile); + } + /** + Maps or unmaps (if file is falsy) the given (sqlite3_file*) + to an xOpen file object and to this pool object. + */ + mapS3FileToOFile(pFile, file) { + if (file) { + this.#mapS3FileToOFile_.set(pFile, file); + setPoolForPFile(pFile, this); + } else { + this.#mapS3FileToOFile_.delete(pFile); + setPoolForPFile(pFile, false); + } + } + /** + Returns true if the given client-defined file name is in this + object's name-to-SAH map. + */ + hasFilename(name) { + return this.#mapFilenameToSAH.has(name); + } + /** + Returns the SAH associated with the given + client-defined file name. + */ + getSAHForPath(path) { + return this.#mapFilenameToSAH.get(path); + } + /** + Removes this object's sqlite3_vfs registration and shuts down + this object, releasing all handles, mappings, and whatnot, + including deleting its data directory. There is currently no + way to "revive" the object and reaquire its + resources. Similarly, there is no recovery strategy if removal + of any given SAH fails, so such errors are ignored by this + function. + + This function is intended primarily for testing. + + Resolves to true if it did its job, false if the + VFS has already been shut down. + + @see pauseVfs() + @see unpauseVfs() + */ + async removeVfs() { + if (!this.#cVfs.pointer || !this.#dhOpaque) return false; + capi.sqlite3_vfs_unregister(this.#cVfs.pointer); + this.#cVfs.dispose(); + delete initPromises[this.vfsName]; + try { + this.releaseAccessHandles(); + await this.#dhVfsRoot.removeEntry(OPAQUE_DIR_NAME, { recursive: true }); + this.#dhOpaque = void 0; + await this.#dhVfsParent.removeEntry(this.#dhVfsRoot.name, { recursive: true }); + this.#dhVfsRoot = this.#dhVfsParent = void 0; + } catch (e) { + sqlite3.config.error(this.vfsName, "removeVfs() failed with no recovery strategy:", e); + } + return true; + } + /** + "Pauses" this VFS by unregistering it from SQLite and + relinquishing all open SAHs, leaving the associated files + intact. If this object is already paused, this is a + no-op. Returns this object. + + This function throws if SQLite has any opened file handles + hosted by this VFS, as the alternative would be to invoke + Undefined Behavior by closing file handles out from under the + library. Similarly, automatically closing any database handles + opened by this VFS would invoke Undefined Behavior in + downstream code which is holding those pointers. + + If this function throws due to open file handles then it has + no side effects. If the OPFS API throws while closing handles + then the VFS is left in an undefined state. + + @see isPaused() + @see unpauseVfs() + */ + pauseVfs() { + if (this.#mapS3FileToOFile_.size > 0) sqlite3.SQLite3Error.toss(capi.SQLITE_MISUSE, "Cannot pause VFS", this.vfsName, "because it has opened files."); + if (this.#mapSAHToName.size > 0) { + capi.sqlite3_vfs_unregister(this.vfsName); + this.releaseAccessHandles(); + } + return this; + } + /** + Returns true if this pool is currently paused else false. + + @see pauseVfs() + @see unpauseVfs() + */ + isPaused() { + return 0 === this.#mapSAHToName.size; + } + /** + "Unpauses" this VFS, reacquiring all SAH's and (if successful) + re-registering it with SQLite. This is a no-op if the VFS is + not currently paused. + + The returned Promise resolves to this object. See + acquireAccessHandles() for how it behaves if it throws due to + SAH acquisition failure. + + @see isPaused() + @see pauseVfs() + */ + async unpauseVfs() { + if (0 === this.#mapSAHToName.size) return this.acquireAccessHandles(false).then(() => capi.sqlite3_vfs_register(this.#cVfs, 0), this); + return this; + } + //! Documented elsewhere in this file. + exportFile(name) { + const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:", name); + const n = sah.getSize() - HEADER_OFFSET_DATA; + const b = new Uint8Array(n > 0 ? n : 0); + if (n > 0) { + const nRead = sah.read(b, { at: HEADER_OFFSET_DATA }); + if (nRead != n) toss("Expected to read " + n + " bytes but read " + nRead + "."); + } + return b; + } + //! Impl for importDb() when its 2nd arg is a function. + async importDbChunked(name, callback) { + const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); + sah.truncate(0); + let nWrote = 0, chunk, checkedHeader = false; + try { + while (void 0 !== (chunk = await callback())) { + if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, { at: HEADER_OFFSET_DATA + nWrote }); + nWrote += chunk.byteLength; + } + if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); + if (!checkedHeader) { + const header = new Uint8Array(20); + sah.read(header, { at: 0 }); + util.affirmDbHeader(header); + } + sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); + } catch (e) { + this.setAssociatedPath(sah, "", 0); + throw e; + } + this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); + return nWrote; + } + //! Documented elsewhere in this file. + importDb(name, bytes) { + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + else if (bytes instanceof Function) return this.importDbChunked(name, bytes); + const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); + const n = bytes.byteLength; + if (n < 512 || n % 512 != 0) toss("Byte array size is invalid for an SQLite db."); + const header = "SQLite format 3"; + for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss("Input does not contain an SQLite database header."); + const nWrote = sah.write(bytes, { at: HEADER_OFFSET_DATA }); + if (nWrote != n) { + this.setAssociatedPath(sah, "", 0); + toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); + } else { + sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); + this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); + } + return nWrote; + } + } + /** + A OpfsSAHPoolUtil instance is exposed to clients in order to + manipulate an OpfsSAHPool object without directly exposing that + object and allowing for some semantic changes compared to that + class. + + Class docs are in the client-level docs for + installOpfsSAHPoolVfs(). + */ + class OpfsSAHPoolUtil { + #p; + constructor(sahPool) { + this.#p = sahPool; + this.vfsName = sahPool.vfsName; + } + async addCapacity(n) { + return this.#p.addCapacity(n); + } + async reduceCapacity(n) { + return this.#p.reduceCapacity(n); + } + getCapacity() { + return this.#p.getCapacity(this.#p); + } + getFileCount() { + return this.#p.getFileCount(); + } + getFileNames() { + return this.#p.getFileNames(); + } + async reserveMinimumCapacity(min) { + const c = this.#p.getCapacity(); + return c < min ? this.#p.addCapacity(min - c) : c; + } + exportFile(name) { + return this.#p.exportFile(name); + } + importDb(name, bytes) { + return this.#p.importDb(name, bytes); + } + async wipeFiles() { + return this.#p.reset(true); + } + unlink(filename) { + return this.#p.deletePath(filename); + } + async removeVfs() { + return this.#p.removeVfs(); + } + pauseVfs() { + this.#p.pauseVfs(); + return this; + } + async unpauseVfs() { + return this.#p.unpauseVfs().then(() => this); + } + isPaused() { + return this.#p.isPaused(); + } + } + /** + Returns a resolved Promise if the current environment + has a "fully-sync" SAH impl, else a rejected Promise. + */ + const apiVersionCheck = async () => { + const dh = await navigator.storage.getDirectory(); + const fn = ".opfs-sahpool-sync-check-" + getRandomName(); + const close = (await (await dh.getFileHandle(fn, { create: true })).createSyncAccessHandle()).close(); + await close; + await dh.removeEntry(fn); + if (close?.then) toss("The local OPFS API is too old for opfs-sahpool:", "it has an async FileSystemSyncAccessHandle.close() method."); + return true; + }; + /** + installOpfsSAHPoolVfs() asynchronously initializes the OPFS + SyncAccessHandle (a.k.a. SAH) Pool VFS. It returns a Promise which + either resolves to a utility object described below or rejects with + an Error value. + + Initialization of this VFS is not automatic because its + registration requires that it lock all resources it + will potentially use, even if client code does not want + to use them. That, in turn, can lead to locking errors + when, for example, one page in a given origin has loaded + this VFS but does not use it, then another page in that + origin tries to use the VFS. If the VFS were automatically + registered, the second page would fail to load the VFS + due to OPFS locking errors. + + If this function is called more than once with a given "name" + option (see below), it will return the same Promise. Calls for + different names will return different Promises which resolve to + independent objects and refer to different VFS registrations. + + On success, the resulting Promise resolves to a utility object + which can be used to query and manipulate the pool. Its API is + described at the end of these docs. + + This function accepts an options object to configure certain + parts but it is only acknowledged for the very first call for + each distinct name and ignored for all subsequent calls with that + same name. + + The options, in alphabetical order: + + - `clearOnInit`: (default=false) if truthy, contents and filename + mapping are removed from each SAH it is acquired during + initialization of the VFS, leaving the VFS's storage in a pristine + state. Use this only for databases which need not survive a page + reload. + + - `initialCapacity`: (default=6) Specifies the default capacity of + the VFS. This should not be set unduly high because the VFS has + to open (and keep open) a file for each entry in the pool. This + setting only has an effect when the pool is initially empty. It + does not have any effect if a pool already exists. + + - `directory`: (default="."+`name`) Specifies the OPFS directory + name in which to store metadata for the `"opfs-sahpool"` + sqlite3_vfs. Only one instance of this VFS can be installed per + JavaScript engine, and any two engines with the same storage + directory name will collide with each other, leading to locking + errors and the inability to register the VFS in the second and + subsequent engine. Using a different directory name for each + application enables different engines in the same HTTP origin to + co-exist, but their data are invisible to each other. Changing + this name will effectively orphan any databases stored under + previous names. The default is unspecified but descriptive. This + option may contain multiple path elements, e.g. "foo/bar/baz", + and they are created automatically. In practice there should be + no driving need to change this. ACHTUNG: all files in this + directory are assumed to be managed by the VFS. Do not place + other files in that directory, as they may be deleted or + otherwise modified by the VFS. + + - `name`: (default="opfs-sahpool") sets the name to register this + VFS under. Normally this should not be changed, but it is + possible to register this VFS under multiple names so long as + each has its own separate directory to work from. The storage for + each is invisible to all others. The name must be a string + compatible with `sqlite3_vfs_register()` and friends and suitable + for use in URI-style database file names. + + Achtung: if a custom `name` is provided, a custom `directory` + must also be provided if any other instance is registered with + the default directory. If no directory is explicitly provided + then a directory name is synthesized from the `name` option. + + + - `forceReinitIfPreviouslyFailed`: (default=`false`) Is a fallback option + to assist in working around certain flaky environments which may + mysteriously fail to permit access to OPFS sync access handles on + an initial attempt but permit it on a second attemp. This option + should never be used but is provided for those who choose to + throw caution to the wind and trust such environments. If this + option is truthy _and_ the previous attempt to initialize this + VFS with the same `name` failed, the VFS will attempt to + initialize a second time instead of returning the cached + failure. See discussion at: + + + + Peculiarities of this VFS vis a vis other SQLite VFSes: + + - Paths given to it _must_ be absolute. Relative paths will not + be properly recognized. This is arguably a bug but correcting it + requires some hoop-jumping in routines which have no business + doing such tricks. (2026-01-19 (2.5 years later): the specifics + are lost to history, but this was a side effect of xOpen() + receiving an immutable C-string filename, to which no implicit + "/" can be prefixed without causing a discrepancy between what + the user provided and what the VFS stores. Its conceivable that + that quirk could be glossed over in xFullPathname(), but + regressions when doing so cannot be ruled out, so there are no + current plans to change this behavior.) + + - It is possible to install multiple instances under different + names, each sandboxed from one another inside their own private + directory. This feature exists primarily as a way for disparate + applications within a given HTTP origin to use this VFS without + introducing locking issues between them. + + + The API for the utility object passed on by this function's + Promise, in alphabetical order... + + - [async] number addCapacity(n) + + Adds `n` entries to the current pool. This change is persistent + across sessions so should not be called automatically at each app + startup (but see `reserveMinimumCapacity()`). Its returned Promise + resolves to the new capacity. Because this operation is necessarily + asynchronous, the C-level VFS API cannot call this on its own as + needed. + + - byteArray exportFile(name) + + Synchronously reads the contents of the given file into a Uint8Array + and returns it. This will throw if the given name is not currently + in active use or on I/O error. Note that the given name is _not_ + visible directly in OPFS (or, if it is, it's not from this VFS). + + - number getCapacity() + + Returns the number of files currently contained + in the SAH pool. The default capacity is only large enough for one + or two databases and their associated temp files. + + - number getFileCount() + + Returns the number of files from the pool currently allocated to + slots. This is not the same as the files being "opened". + + - array getFileNames() + + Returns an array of the names of the files currently allocated to + slots. This list is the same length as getFileCount(). + + - void importDb(name, bytes) + + Imports the contents of an SQLite database, provided as a byte + array or ArrayBuffer, under the given name, overwriting any + existing content. Throws if the pool has no available file slots, + on I/O error, or if the input does not appear to be a + database. In the latter case, only a cursory examination is made. + Results are undefined if the given db name refers to an opened + db. Note that this routine is _only_ for importing database + files, not arbitrary files, the reason being that this VFS will + automatically clean up any non-database files so importing them + is pointless. + + If passed a function for its second argument, its behavior + changes to asynchronous and it imports its data in chunks fed to + it by the given callback function. It calls the callback (which + may be async) repeatedly, expecting either a Uint8Array or + ArrayBuffer (to denote new input) or undefined (to denote + EOF). For so long as the callback continues to return + non-undefined, it will append incoming data to the given + VFS-hosted database file. The result of the resolved Promise when + called this way is the size of the resulting database. + + On success this routine rewrites the database header bytes in the + output file (not the input array) to force disabling of WAL mode. + + On a write error, the handle is removed from the pool and made + available for re-use. + + - [async] number reduceCapacity(n) + + Removes up to `n` entries from the pool, with the caveat that it can + only remove currently-unused entries. It returns a Promise which + resolves to the number of entries actually removed. + + - [async] boolean removeVfs() + + Unregisters the opfs-sahpool VFS and removes its directory from OPFS + (which means that _all client content_ is removed). After calling + this, the VFS may no longer be used and there is no way to re-add it + aside from reloading the current JavaScript context. + + Results are undefined if a database is currently in use with this + VFS. + + The returned Promise resolves to true if it performed the removal + and false if the VFS was not installed. + + If the VFS has a multi-level directory, e.g. "/foo/bar/baz", _only_ + the bottom-most directory is removed because this VFS cannot know for + certain whether the higher-level directories contain data which + should be removed. + + - [async] number reserveMinimumCapacity(min) + + If the current capacity is less than `min`, the capacity is + increased to `min`, else this returns with no side effects. The + resulting Promise resolves to the new capacity. + + - boolean unlink(filename) + + If a virtual file exists with the given name, disassociates it from + the pool and returns true, else returns false without side + effects. Results are undefined if the file is currently in active + use. + + - string vfsName + + The SQLite VFS name under which this pool's VFS is registered. + + - [async] void wipeFiles() + + Clears all client-defined state of all SAHs and makes all of them + available for re-use by the pool. Results are undefined if any such + handles are currently in use, e.g. by an sqlite3 db. + + APIs specific to the "pause" capability (added in version 3.49): + + Summary: "pausing" the VFS disassociates it from SQLite and + relinquishes its SAHs so that they may be opened by another + instance of this VFS (running in a separate tab/page or Worker). + "Unpausing" it takes back control, if able. + + - pauseVfs() + + "Pauses" this VFS by unregistering it from SQLite and + relinquishing all open SAHs, leaving the associated files intact. + This enables pages/tabs to coordinate semi-concurrent usage of + this VFS. If this object is already paused, this is a + no-op. Returns this object. Throws if SQLite has any opened file + handles hosted by this VFS. If this function throws due to open + file handles then it has no side effects. If the OPFS API throws + while closing handles then the VFS is left in an undefined state. + + - isPaused() + + Returns true if this VFS is paused, else false. + + - [async] unpauseVfs() + + Restores the VFS to an active state after having called + pauseVfs() on it. This is a no-op if the VFS is not paused. The + returned Promise resolves to this object on success. A rejected + Promise means there was a problem reacquiring the SAH handles + (possibly because they're in use by another instance or have + since been removed). Generically speaking, there is no recovery + strategy for that type of error, but if the problem is simply + that the OPFS files are locked, then a later attempt to unpause + it, made after the concurrent instance releases the SAHs, may + recover from the situation. + */ + sqlite3.installOpfsSAHPoolVfs = async function(options = Object.create(null)) { + options = Object.assign(Object.create(null), optionDefaults, options || {}); + const vfsName = options.name; + if (options.$testThrowPhase1) throw options.$testThrowPhase1; + if (initPromises[vfsName]) try { + return await initPromises[vfsName]; + } catch (e) { + if (options.forceReinitIfPreviouslyFailed) delete initPromises[vfsName]; + else throw e; + } + if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return initPromises[vfsName] = Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); + /** + Maintenance reminder: the order of ASYNC ops in this function + is significant. We need to have them all chained at the very + end in order to be able to catch a race condition where + installOpfsSAHPoolVfs() is called twice in rapid succession, + e.g.: + + installOpfsSAHPoolVfs().then(console.warn.bind(console)); + installOpfsSAHPoolVfs().then(console.warn.bind(console)); + + If the timing of the async calls is not "just right" then that + second call can end up triggering the init a second time and chaos + ensues. + */ + return initPromises[vfsName] = apiVersionCheck().then(async function() { + if (options.$testThrowPhase2) throw options.$testThrowPhase2; + const thePool = new OpfsSAHPool(options); + return thePool.isReady.then(async () => { + /** The poolUtil object will be the result of the + resolved Promise. */ + const poolUtil = new OpfsSAHPoolUtil(thePool); + if (sqlite3.oo1) { + const oo1 = sqlite3.oo1; + const theVfs = thePool.getVfs(); + const OpfsSAHPoolDb = function(...args) { + const opt = oo1.DB.dbCtorHelper.normalizeArgs(...args); + opt.vfs = theVfs.$zName; + oo1.DB.dbCtorHelper.call(this, opt); + }; + OpfsSAHPoolDb.prototype = Object.create(oo1.DB.prototype); + poolUtil.OpfsSAHPoolDb = OpfsSAHPoolDb; + } + thePool.log("VFS initialized."); + return poolUtil; + }).catch(async (e) => { + await thePool.removeVfs().catch(() => {}); + throw e; + }); + }).catch((err) => { + return initPromises[vfsName] = Promise.reject(err); + }); + }; + }); + try { + const bootstrapConfig = Object.assign( + Object.create(null), + /** The WASM-environment-dependent configuration for sqlite3ApiBootstrap() */ + { + memory: "undefined" !== typeof wasmMemory ? wasmMemory : EmscriptenModule["wasmMemory"], + exports: "undefined" !== typeof wasmExports ? wasmExports : Object.prototype.hasOwnProperty.call(EmscriptenModule, "wasmExports") ? EmscriptenModule["wasmExports"] : EmscriptenModule["asm"] + }, + globalThis.sqlite3ApiBootstrap.defaultConfig, + globalThis.sqlite3ApiConfig || {} + ); + sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig); + /** + For purposes of the Emscripten build, call sqlite3ApiBootstrap(). + Ideally clients should be able to inject their own config here, + but that's not practical in this particular build constellation + because of the order everything happens in. Clients may either + define globalThis.sqlite3ApiConfig or modify + globalThis.sqlite3ApiBootstrap.defaultConfig to tweak the default + configuration used by a no-args call to sqlite3ApiBootstrap(), + but must have first loaded their WASM module in order to be able + to provide the necessary configuration state. + */ + const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig); + delete globalThis.sqlite3ApiBootstrap; + return p; + } catch (e) { + console.error("sqlite3ApiBootstrap() error:", e); + throw e; + } + }; + if (runtimeInitialized) moduleRtn = Module; + else moduleRtn = new Promise((resolve, reject) => { + readyPromiseResolve = resolve; + readyPromiseReject = reject; + }); + return moduleRtn; +} +sqlite3InitModule = (function() { + /** + In order to hide the sqlite3InitModule()'s resulting + Emscripten module from downstream clients (and simplify our + documentation by being able to elide those details), we hide that + function and expose a hand-written sqlite3InitModule() to return + the sqlite3 object (most of the time). + */ + const originalInit = sqlite3InitModule; + if (!originalInit) throw new Error("Expecting sqlite3InitModule to be defined by the Emscripten build."); + /** + We need to add some state which our custom Module.locateFile() + can see, but an Emscripten limitation currently prevents us from + attaching it to the sqlite3InitModule function object: + + https://github.com/emscripten-core/emscripten/issues/18071 + + The only(?) current workaround is to temporarily stash this state + into the global scope and delete it when sqlite3InitModule() + is called. + */ + const sIMS = globalThis.sqlite3InitModuleState = Object.assign(Object.create(null), { + moduleScript: globalThis?.document?.currentScript, + isWorker: "undefined" !== typeof WorkerGlobalScope, + location: globalThis.location, + urlParams: globalThis?.location?.href ? new URL(globalThis.location.href).searchParams : new URLSearchParams(), + wasmFilename: "sqlite3.wasm" + }); + sIMS.debugModule = sIMS.urlParams.has("sqlite3.debugModule") ? (...args) => console.warn("sqlite3.debugModule:", ...args) : () => {}; + if (sIMS.urlParams.has("sqlite3.dir")) sIMS.sqlite3Dir = sIMS.urlParams.get("sqlite3.dir") + "/"; + else if (sIMS.moduleScript) { + const li = sIMS.moduleScript.src.split("/"); + li.pop(); + sIMS.sqlite3Dir = li.join("/") + "/"; + } + const sIM = globalThis.sqlite3InitModule = function ff(...args) { + sIMS.emscriptenLocateFile = args[0]?.locateFile; + sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm; + return originalInit(...args).then((EmscriptenModule) => { + sIMS.debugModule("sqlite3InitModule() sIMS =", sIMS); + sIMS.debugModule("sqlite3InitModule() EmscriptenModule =", EmscriptenModule); + const s = EmscriptenModule.runSQLite3PostLoadInit(sIMS, EmscriptenModule, !!ff.__isUnderTest); + sIMS.debugModule("sqlite3InitModule() sqlite3 =", s); + return s; + }).catch((e) => { + console.error("Exception loading sqlite3 module:", e); + throw e; + }); + }; + sIM.ready = originalInit.ready; + if (sIMS.moduleScript) { + let src = sIMS.moduleScript.src.split("/"); + src.pop(); + sIMS.scriptDir = src.join("/") + "/"; + } + sIMS.debugModule("extern-post-js.c-pp.js sqlite3InitModuleState =", sIMS); + return sIM; +})(); +var sqlite3_bundler_friendly_default = sqlite3InitModule; +//#endregion +export { sqlite3_bundler_friendly_default as default, sqlite3_worker1_promiser_default as sqlite3Worker1Promiser }; diff --git a/src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js b/src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js new file mode 100644 index 00000000..57a8feb7 --- /dev/null +++ b/src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js @@ -0,0 +1,666 @@ +"use strict"; +(function() { + //#region src/bin/sqlite3-opfs-async-proxy.js + /* @preserve + 2022-09-16 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + A Worker which manages asynchronous OPFS handles on behalf of a + synchronous API which controls it via a combination of Worker + messages, SharedArrayBuffer, and Atomics. It is the asynchronous + counterpart of the API defined in sqlite3-vfs-opfs.js. + + Highly indebted to: + + https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/OriginPrivateFileSystemVFS.js + + for demonstrating how to use the OPFS APIs. + + This file is to be loaded as a Worker. It does not have any direct + access to the sqlite3 JS/WASM bits, so any bits which it needs (most + notably SQLITE_xxx integer codes) have to be imported into it via an + initialization process. + + This file represents an implementation detail of a larger piece of + code, and not a public interface. Its details may change at any time + and are not intended to be used by any client-level code. + + 2022-11-27: Chrome v108 changes some async methods to synchronous, as + documented at: + + https://developer.chrome.com/blog/sync-methods-for-accesshandles/ + + Firefox v111 and Safari 16.4, both released in March 2023, also + include this. + + We cannot change to the sync forms at this point without breaking + clients who use Chrome v104-ish or higher. truncate(), getSize(), + flush(), and close() are now (as of v108) synchronous. Calling them + with an "await", as we have to for the async forms, is still legal + with the sync forms but is superfluous. Calling the async forms with + theFunc().then(...) is not compatible with the change to + synchronous, but we do do not use those APIs that way. i.e. we don't + _need_ to change anything for this, but at some point (after Chrome + versions (approximately) 104-107 are extinct) should change our + usage of those methods to remove the "await". + */ + const wPost = (type, ...args) => postMessage({ + type, + payload: args + }); + const installAsyncProxy = function() { + const toss = function(...args) { + throw new Error(args.join(" ")); + }; + if (globalThis.window === globalThis) toss("This code cannot run from the main thread.", "Load it as a Worker from a separate Worker."); + else if (!navigator?.storage?.getDirectory) toss("This API requires navigator.storage.getDirectory."); + /** + Will hold state copied to this object from the synchronous side of + this API. + */ + const state = Object.create(null); + /** + verbose: + + 0 = no logging output + 1 = only errors + 2 = warnings and errors + 3 = debug, warnings, and errors + */ + state.verbose = 1; + const loggers = { + 0: console.error.bind(console), + 1: console.warn.bind(console), + 2: console.log.bind(console) + }; + const logImpl = (level, ...args) => { + if (state.verbose > level) loggers[level]("OPFS asyncer:", ...args); + }; + const log = (...args) => logImpl(2, ...args); + const warn = (...args) => logImpl(1, ...args); + const error = (...args) => logImpl(0, ...args); + /** + __openFiles is a map of sqlite3_file pointers (integers) to + metadata related to a given OPFS file handles. The pointers are, in + this side of the interface, opaque file handle IDs provided by the + synchronous part of this constellation. Each value is an object + with a structure demonstrated in the xOpen() impl. + */ + const __openFiles = Object.create(null); + /** + __implicitLocks is a Set of sqlite3_file pointers (integers) which were + "auto-locked". i.e. those for which we obtained a sync access + handle without an explicit xLock() call. Such locks will be + released during db connection idle time, whereas a sync access + handle obtained via xLock(), or subsequently xLock()'d after + auto-acquisition, will not be released until xUnlock() is called. + + Maintenance reminder: if we relinquish auto-locks at the end of the + operation which acquires them, we pay a massive performance + penalty: speedtest1 benchmarks take up to 4x as long. By delaying + the lock release until idle time, the hit is negligible. + */ + const __implicitLocks = /* @__PURE__ */ new Set(); + /** + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd arg is + true, the result is returned as an array of path elements, else an + absolute path string is returned. + */ + const getResolvedPath = function(filename, splitIt) { + const p = new URL(filename, "file://irrelevant").pathname; + return splitIt ? p.split("/").filter((v) => !!v) : p; + }; + /** + Takes the absolute path to a filesystem element. Returns an array + of [handleOfContainingDir, filename]. If the 2nd argument is truthy + then each directory element leading to the file is created along + the way. Throws if any creation or resolution fails. + */ + const getDirForFilename = async function f(absFilename, createDirs = false) { + const path = getResolvedPath(absFilename, true); + const filename = path.pop(); + let dh = state.rootDir; + for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); + return [dh, filename]; + }; + /** + If the given file-holding object has a sync handle attached to it, + that handle is removed and asynchronously closed. Though it may + sound sensible to continue work as soon as the close() returns + (noting that it's asynchronous), doing so can cause operations + performed soon afterwards, e.g. a call to getSyncHandle(), to fail + because they may happen out of order from the close(). OPFS does + not guaranty that the actual order of operations is retained in + such cases. i.e. always "await" on the result of this function. + */ + const closeSyncHandle = async (fh) => { + if (fh.syncHandle) { + log("Closing sync handle for", fh.filenameAbs); + const h = fh.syncHandle; + delete fh.syncHandle; + delete fh.xLock; + __implicitLocks.delete(fh.fid); + return h.close(); + } + }; + /** + A proxy for closeSyncHandle() which is guaranteed to not throw. + + This function is part of a lock/unlock step in functions which + require a sync access handle but may be called without xLock() + having been called first. Such calls need to release that + handle to avoid locking the file for all of time. This is an + _attempt_ at reducing cross-tab contention but it may prove + to be more of a problem than a solution and may need to be + removed. + */ + const closeSyncHandleNoThrow = async (fh) => { + try { + await closeSyncHandle(fh); + } catch (e) { + warn("closeSyncHandleNoThrow() ignoring:", e, fh); + } + }; + const releaseImplicitLocks = async () => { + if (__implicitLocks.size) for (const fid of __implicitLocks) { + const fh = __openFiles[fid]; + await closeSyncHandleNoThrow(fh); + log("Auto-unlocked", fid, fh.filenameAbs); + } + }; + /** + An experiment in improving concurrency by freeing up implicit locks + sooner. This is known to impact performance dramatically but it has + also shown to improve concurrency considerably. + + If fh.releaseImplicitLocks is truthy and fh is in __implicitLocks, + this routine returns closeSyncHandleNoThrow(), else it is a no-op. + */ + const releaseImplicitLock = async (fh) => { + if (fh.releaseImplicitLocks && __implicitLocks.has(fh.fid)) return closeSyncHandleNoThrow(fh); + }; + /** + An error class specifically for use with getSyncHandle(), the goal + of which is to eventually be able to distinguish unambiguously + between locking-related failures and other types, noting that we + cannot currently do so because createSyncAccessHandle() does not + define its exceptions in the required level of detail. + + 2022-11-29: according to: + + https://github.com/whatwg/fs/pull/21 + + NoModificationAllowedError will be the standard exception thrown + when acquisition of a sync access handle fails due to a locking + error. As of this writing, that error type is not visible in the + dev console in Chrome v109, nor is it documented in MDN, but an + error with that "name" property is being thrown from the OPFS + layer. + */ + class GetSyncHandleError extends Error { + constructor(errorObject, ...msg) { + super([ + ...msg, + ": " + errorObject.name + ":", + errorObject.message + ].join(" "), { cause: errorObject }); + this.name = "GetSyncHandleError"; + } + } + /** + Attempts to find a suitable SQLITE_xyz result code for Error + object e. Returns either such a translation or rc if if it does + not know how to translate the exception. + */ + GetSyncHandleError.convertRc = (e, rc) => { + if (e instanceof GetSyncHandleError) { + if (e.cause.name === "NoModificationAllowedError" || e.cause.name === "DOMException" && 0 === e.cause.message.indexOf("Access Handles cannot")) return state.sq3Codes.SQLITE_BUSY; + else if ("NotFoundError" === e.cause.name) + /** + Maintenance reminder: SQLITE_NOTFOUND, though it looks like + a good match, has different semantics than NotFoundError + and is not suitable here. + */ + return state.sq3Codes.SQLITE_CANTOPEN; + } else if ("NotFoundError" === e?.name) return state.sq3Codes.SQLITE_CANTOPEN; + return rc; + }; + /** + Returns the sync access handle associated with the given file + handle object (which must be a valid handle object, as created by + xOpen()), lazily opening it if needed. + + In order to help alleviate cross-tab contention for a dabase, if + an exception is thrown while acquiring the handle, this routine + will wait briefly and try again, up to some fixed number of + times. If acquisition still fails at that point it will give up + and propagate the exception. Client-level code will see that as + an I/O error. + + 2024-06-12: there is a rare race condition here which has been + reported a single time: + + https://sqlite.org/forum/forumpost/9ee7f5340802d600 + + What appears to be happening is that file we're waiting for a + lock on is deleted while we wait. What currently happens here is + that a locking exception is thrown but the exception type is + NotFoundError. In such cases, we very probably should attempt to + re-open/re-create the file an obtain the lock on it (noting that + there's another race condition there). That's easy to say but + creating a viable test for that condition has proven challenging + so far. + */ + const getSyncHandle = async (fh, opName) => { + if (!fh.syncHandle) { + const t = performance.now(); + log("Acquiring sync handle for", fh.filenameAbs); + const maxTries = 6, msBase = state.asyncIdleWaitTime * 2; + let i = 1, ms = msBase; + for (;; ms = msBase * ++i) try { + fh.syncHandle = await fh.fileHandle.createSyncAccessHandle(); + break; + } catch (e) { + if (i === maxTries) throw new GetSyncHandleError(e, "Error getting sync handle for", opName + "().", maxTries, "attempts failed.", fh.filenameAbs); + warn("Error getting sync handle for", opName + "(). Waiting", ms, "ms and trying again.", fh.filenameAbs, e); + Atomics.wait(state.sabOPView, state.opIds.retry, 0, ms); + } + log("Got", opName + "() sync handle for", fh.filenameAbs, "in", performance.now() - t, "ms"); + if (!fh.xLock) { + __implicitLocks.add(fh.fid); + log("Acquired implicit lock for", opName + "()", fh.fid, fh.filenameAbs); + } + } + return fh.syncHandle; + }; + /** + Stores the given value at state.sabOPView[state.opIds.rc] and then + Atomics.notify()'s it. + */ + const storeAndNotify = (opName, value) => { + log(opName + "() => notify(", value, ")"); + Atomics.store(state.sabOPView, state.opIds.rc, value); + Atomics.notify(state.sabOPView, state.opIds.rc); + }; + /** + Throws if fh is a file-holding object which is flagged as read-only. + */ + const affirmNotRO = function(opName, fh) { + if (fh.readOnly) toss(opName + "(): File is read-only: " + fh.filenameAbs); + }; + /** + Gets set to true by the 'opfs-async-shutdown' command to quit the + wait loop. This is only intended for debugging purposes: we cannot + inspect this file's state while the tight waitLoop() is running and + need a way to stop that loop for introspection purposes. + */ + let flagAsyncShutdown = false; + /** + Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods + methods, as well as helpers like mkdir(). + */ + const vfsAsyncImpls = { + "opfs-async-shutdown": async () => { + flagAsyncShutdown = true; + storeAndNotify("opfs-async-shutdown", 0); + }, + mkdir: async (dirname) => { + let rc = 0; + try { + await getDirForFilename(dirname + "/filepart", true); + } catch (e) { + state.s11n.storeException(2, e); + rc = state.sq3Codes.SQLITE_IOERR; + } + storeAndNotify("mkdir", rc); + }, + xAccess: async (filename) => { + let rc = 0; + try { + const [dh, fn] = await getDirForFilename(filename); + await dh.getFileHandle(fn); + } catch (e) { + state.s11n.storeException(2, e); + rc = state.sq3Codes.SQLITE_IOERR; + } + storeAndNotify("xAccess", rc); + }, + xClose: async function(fid) { + const opName = "xClose"; + __implicitLocks.delete(fid); + const fh = __openFiles[fid]; + let rc = 0; + if (fh) { + delete __openFiles[fid]; + await closeSyncHandle(fh); + if (fh.deleteOnClose) try { + await fh.dirHandle.removeEntry(fh.filenamePart); + } catch (e) { + warn("Ignoring dirHandle.removeEntry() failure of", fh, e); + } + } else { + state.s11n.serialize(); + rc = state.sq3Codes.SQLITE_NOTFOUND; + } + storeAndNotify(opName, rc); + }, + xDelete: async function(...args) { + storeAndNotify("xDelete", await vfsAsyncImpls.xDeleteNoWait(...args)); + }, + xDeleteNoWait: async function(filename, syncDir = 0, recursive = false) { + let rc = 0; + try { + while (filename) { + const [hDir, filenamePart] = await getDirForFilename(filename, false); + if (!filenamePart) break; + await hDir.removeEntry(filenamePart, { recursive }); + if (4660 !== syncDir) break; + recursive = false; + filename = getResolvedPath(filename, true); + filename.pop(); + filename = filename.join("/"); + } + } catch (e) { + state.s11n.storeException(2, e); + rc = state.sq3Codes.SQLITE_IOERR_DELETE; + } + return rc; + }, + xFileSize: async function(fid) { + const fh = __openFiles[fid]; + let rc = 0; + try { + const sz = await (await getSyncHandle(fh, "xFileSize")).getSize(); + state.s11n.serialize(Number(sz)); + } catch (e) { + state.s11n.storeException(1, e); + rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR); + } + await releaseImplicitLock(fh); + storeAndNotify("xFileSize", rc); + }, + xLock: async function(fid, lockType) { + const fh = __openFiles[fid]; + let rc = 0; + const oldLockType = fh.xLock; + fh.xLock = lockType; + if (!fh.syncHandle) try { + await getSyncHandle(fh, "xLock"); + __implicitLocks.delete(fid); + } catch (e) { + state.s11n.storeException(1, e); + rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_LOCK); + fh.xLock = oldLockType; + } + storeAndNotify("xLock", rc); + }, + xOpen: async function(fid, filename, flags, opfsFlags) { + const opName = "xOpen"; + const create = state.sq3Codes.SQLITE_OPEN_CREATE & flags; + try { + let hDir, filenamePart; + try { + [hDir, filenamePart] = await getDirForFilename(filename, !!create); + } catch (e) { + state.s11n.storeException(1, e); + storeAndNotify(opName, state.sq3Codes.SQLITE_NOTFOUND); + return; + } + if (state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN & opfsFlags) try { + await hDir.removeEntry(filenamePart); + } catch (e) {} + const hFile = await hDir.getFileHandle(filenamePart, { create }); + const fh = Object.assign(Object.create(null), { + fid, + filenameAbs: filename, + filenamePart, + dirHandle: hDir, + fileHandle: hFile, + sabView: state.sabFileBufView, + readOnly: !create && !!(state.sq3Codes.SQLITE_OPEN_READONLY & flags), + deleteOnClose: !!(state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags) + }); + fh.releaseImplicitLocks = opfsFlags & state.opfsFlags.OPFS_UNLOCK_ASAP || state.opfsFlags.defaultUnlockAsap; + __openFiles[fid] = fh; + storeAndNotify(opName, 0); + } catch (e) { + error(opName, e); + state.s11n.storeException(1, e); + storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR); + } + }, + xRead: async function(fid, n, offset64) { + let rc = 0, nRead; + const fh = __openFiles[fid]; + try { + nRead = (await getSyncHandle(fh, "xRead")).read(fh.sabView.subarray(0, n), { at: Number(offset64) }); + if (nRead < n) { + fh.sabView.fill(0, nRead, n); + rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; + } + } catch (e) { + error("xRead() failed", e, fh); + state.s11n.storeException(1, e); + rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_READ); + } + await releaseImplicitLock(fh); + storeAndNotify("xRead", rc); + }, + xSync: async function(fid, flags) { + const fh = __openFiles[fid]; + let rc = 0; + if (!fh.readOnly && fh.syncHandle) try { + await fh.syncHandle.flush(); + } catch (e) { + state.s11n.storeException(2, e); + rc = state.sq3Codes.SQLITE_IOERR_FSYNC; + } + storeAndNotify("xSync", rc); + }, + xTruncate: async function(fid, size) { + let rc = 0; + const fh = __openFiles[fid]; + try { + affirmNotRO("xTruncate", fh); + await (await getSyncHandle(fh, "xTruncate")).truncate(size); + } catch (e) { + error("xTruncate():", e, fh); + state.s11n.storeException(2, e); + rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_TRUNCATE); + } + await releaseImplicitLock(fh); + storeAndNotify("xTruncate", rc); + }, + xUnlock: async function(fid, lockType) { + let rc = 0; + const fh = __openFiles[fid]; + if (fh.syncHandle && state.sq3Codes.SQLITE_LOCK_NONE === lockType) try { + await closeSyncHandle(fh); + } catch (e) { + state.s11n.storeException(1, e); + rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; + } + storeAndNotify("xUnlock", rc); + }, + xWrite: async function(fid, n, offset64) { + let rc; + const fh = __openFiles[fid]; + try { + affirmNotRO("xWrite", fh); + rc = n === (await getSyncHandle(fh, "xWrite")).write(fh.sabView.subarray(0, n), { at: Number(offset64) }) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; + } catch (e) { + error("xWrite():", e, fh); + state.s11n.storeException(1, e); + rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_WRITE); + } + await releaseImplicitLock(fh); + storeAndNotify("xWrite", rc); + } + }; + const initS11n = () => { + /** + ACHTUNG: this code is 100% duplicated in the other half of this + proxy! The documentation is maintained in the "synchronous half". + */ + if (state.s11n) return state.s11n; + const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + state.s11n = Object.create(null); + const TypeIds = Object.create(null); + TypeIds.number = { + id: 1, + size: 8, + getter: "getFloat64", + setter: "setFloat64" + }; + TypeIds.bigint = { + id: 2, + size: 8, + getter: "getBigInt64", + setter: "setBigInt64" + }; + TypeIds.boolean = { + id: 3, + size: 4, + getter: "getInt32", + setter: "setInt32" + }; + TypeIds.string = { id: 4 }; + const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v); + const getTypeIdById = (tid) => { + switch (tid) { + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:", tid); + } + }; + state.s11n.deserialize = function(clear = false) { + const argc = viewU8[0]; + const rc = argc ? [] : null; + if (argc) { + const typeIds = []; + let offset = 1, i, n, v; + for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset])); + for (i = 0; i < argc; ++i) { + const t = typeIds[i]; + if (t.getter) { + v = viewDV[t.getter](offset, state.littleEndian); + offset += t.size; + } else { + n = viewDV.getInt32(offset, state.littleEndian); + offset += 4; + v = textDecoder.decode(viewU8.slice(offset, offset + n)); + offset += n; + } + rc.push(v); + } + } + if (clear) viewU8[0] = 0; + return rc; + }; + state.s11n.serialize = function(...args) { + if (args.length) { + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 255; + for (; i < args.length; ++i, ++offset) { + typeIds.push(getTypeId(args[i])); + viewU8[offset] = typeIds[i].id; + } + for (i = 0; i < args.length; ++i) { + const t = typeIds[i]; + if (t.setter) { + viewDV[t.setter](offset, args[i], state.littleEndian); + offset += t.size; + } else { + const s = textEncoder.encode(args[i]); + viewDV.setInt32(offset, s.byteLength, state.littleEndian); + offset += 4; + viewU8.set(s, offset); + offset += s.byteLength; + } + } + } else viewU8[0] = 0; + }; + state.s11n.storeException = state.asyncS11nExceptions ? ((priority, e) => { + if (priority <= state.asyncS11nExceptions) state.s11n.serialize([ + e.name, + ": ", + e.message + ].join("")); + }) : () => {}; + return state.s11n; + }; + const waitLoop = async function f() { + const opHandlers = Object.create(null); + for (let k of Object.keys(state.opIds)) { + const vi = vfsAsyncImpls[k]; + if (!vi) continue; + const o = Object.create(null); + opHandlers[state.opIds[k]] = o; + o.key = k; + o.f = vi; + } + while (!flagAsyncShutdown) try { + if ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.whichOp, 0, state.asyncIdleWaitTime)) { + await releaseImplicitLocks(); + continue; + } + const opId = Atomics.load(state.sabOPView, state.opIds.whichOp); + Atomics.store(state.sabOPView, state.opIds.whichOp, 0); + const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #", opId); + const args = state.s11n.deserialize(true) || []; + if (hnd.f) await hnd.f(...args); + else error("Missing callback for opId", opId); + } catch (e) { + error("in waitLoop():", e); + } + }; + navigator.storage.getDirectory().then(function(d) { + state.rootDir = d; + globalThis.onmessage = function({ data }) { + switch (data.type) { + case "opfs-async-init": { + const opt = data.args; + for (const k in opt) state[k] = opt[k]; + state.verbose = opt.verbose ?? 1; + state.sabOPView = new Int32Array(state.sabOP); + state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); + state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + Object.keys(vfsAsyncImpls).forEach((k) => { + if (!Number.isFinite(state.opIds[k])) toss("Maintenance required: missing state.opIds[", k, "]"); + }); + initS11n(); + log("init state", state); + wPost("opfs-async-inited"); + waitLoop(); + break; + } + case "opfs-async-restart": + if (flagAsyncShutdown) { + warn("Restarting after opfs-async-shutdown. Might or might not work."); + flagAsyncShutdown = false; + waitLoop(); + } + break; + } + }; + wPost("opfs-async-loaded"); + }).catch((e) => error("error initializing OPFS asyncer:", e)); + }; + if (!globalThis.SharedArrayBuffer) wPost("opfs-unavailable", "Missing SharedArrayBuffer API.", "The server must emit the COOP/COEP response headers to enable that."); + else if (!globalThis.Atomics) wPost("opfs-unavailable", "Missing Atomics API.", "The server must emit the COOP/COEP response headers to enable that."); + else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) wPost("opfs-unavailable", "Missing required OPFS APIs."); + else installAsyncProxy(); + //#endregion +})(); diff --git a/src/web/sqlite-wasm/sqlite3-worker1.mjs b/src/web/sqlite-wasm/sqlite3-worker1.mjs new file mode 100644 index 00000000..533b4e9a --- /dev/null +++ b/src/web/sqlite-wasm/sqlite3-worker1.mjs @@ -0,0 +1,15461 @@ +//#region src/bin/sqlite3.mjs +/* @preserve +** +** LICENSE for the sqlite3 WebAssembly/JavaScript APIs. +** +** This bundle (typically released as sqlite3.js or sqlite3.mjs) +** is an amalgamation of JavaScript source code from two projects: +** +** 1) https://emscripten.org: the Emscripten "glue code" is covered by +** the terms of the MIT license and University of Illinois/NCSA +** Open Source License, as described at: +** +** https://emscripten.org/docs/introducing_emscripten/emscripten_license.html +** +** 2) https://sqlite.org: all code and documentation labeled as being +** from this source are released under the same terms as the sqlite3 +** C library: +** +** 2022-10-16 +** +** The author disclaims copyright to this source code. In place of a +** legal notice, here is a blessing: +** +** * May you do good and not evil. +** * May you find forgiveness for yourself and forgive others. +** * May you share freely, never taking more than you give. +*/ +/* @preserve +** This code was built from sqlite3 version... +** +** SQLITE_VERSION "3.52.0" +** SQLITE_VERSION_NUMBER 3052000 +** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** +** Emscripten SDK: 5.0.0 +*/ +async function sqlite3InitModule(moduleArg = {}) { + var moduleRtn; + var Module = moduleArg; + var ENVIRONMENT_IS_WEB = !!globalThis.window; + var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; + globalThis.process?.versions?.node && globalThis.process?.type; + /** + BEGIN FILE: api/pre-js.js + + This file is intended to be prepended to the sqlite3.js build using + Emscripten's --pre-js=THIS_FILE flag (or equivalent). It is run + from inside of sqlite3InitModule(), after Emscripten's Module is + defined, but early enough that we can ammend, or even outright + replace, Module from here. + + Because this runs in-between Emscripten's own bootstrapping and + Emscripten's main work, we must be careful with file-local symbol + names. e.g. don't overwrite anything Emscripten defines and do not + use 'const' for local symbols which Emscripten might try to use for + itself. i.e. try to keep file-local symbol names obnoxiously + collision-resistant. + */ + /** + This file was preprocessed using: + + ./c-pp-lite -o ./bld/pre-js.esm.js -Dtarget:es6-module -DModule.instantiateWasm api/pre-js.c-pp.js + */ + (function(Module) { + const sIMS = globalThis.sqlite3InitModuleState || Object.assign(Object.create(null), { debugModule: function() { + console.warn("globalThis.sqlite3InitModuleState is missing", arguments); + } }); + delete globalThis.sqlite3InitModuleState; + sIMS.debugModule("pre-js.js sqlite3InitModuleState =", sIMS); + /** + This custom locateFile() tries to figure out where to load `path` + from. The intent is to provide a way for foo/bar/X.js loaded from a + Worker constructor or importScripts() to be able to resolve + foo/bar/X.wasm (in the latter case, with some help): + + 1) If URL param named the same as `path` is set, it is returned. + + 2) If sqlite3InitModuleState.sqlite3Dir is set, then (thatName + path) + is returned (it's assumed to end with '/'). + + 3) If this code is running in the main UI thread AND it was loaded + from a SCRIPT tag, the directory part of that URL is used + as the prefix. (This form of resolution unfortunately does not + function for scripts loaded via importScripts().) + + 4) If none of the above apply, (prefix+path) is returned. + + None of the above apply in ES6 builds, which uses a much simpler + approach. + */ + Module["locateFile"] = function(path, prefix) { + if (this.emscriptenLocateFile instanceof Function) return this.emscriptenLocateFile(path, prefix); + return new URL(path, import.meta.url).href; + }.bind(sIMS); + /** + Override Module.instantiateWasm(). + + A custom Module.instantiateWasm() does not work in WASMFS builds: + + https://github.com/emscripten-core/emscripten/issues/17951 + + In such builds we must disable this. + + It's disabled in the (unsupported/untested) node builds because + node does not do fetch(). + */ + Module["instantiateWasm"] = function callee(imports, onSuccess) { + if (this.emscriptenInstantiateWasm instanceof Function) return this.emscriptenInstantiateWasm(imports, onSuccess); + const sims = this; + const uri = Module.locateFile(sims.wasmFilename, "undefined" === typeof scriptDirectory ? "" : scriptDirectory); + sims.debugModule("instantiateWasm() uri =", uri, "sIMS =", this); + const wfetch = () => fetch(uri, { credentials: "same-origin" }); + const finalThen = (arg) => { + arg.imports = imports; + sims.instantiateWasm = arg; + onSuccess(arg.instance, arg.module); + }; + return (WebAssembly.instantiateStreaming ? async () => WebAssembly.instantiateStreaming(wfetch(), imports).then(finalThen) : async () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, imports)).then(finalThen))(); + }.bind(sIMS); + })(Module); + var thisProgram = "./this.program"; + var _scriptName = import.meta.url; + 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) { + try { + scriptDirectory = new URL(".", _scriptName).href; + } catch {} + 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 = async (url) => { + var response = await fetch(url, { credentials: "same-origin" }); + if (response.ok) return response.arrayBuffer(); + throw new Error(response.status + " : " + response.url); + }; + } + var out = console.log.bind(console); + var err = console.error.bind(console); + var wasmBinary; + var ABORT = false, readyPromiseResolve, readyPromiseReject, HEAP8, HEAPU8, HEAP16, HEAP32, HEAPU32, HEAP64; + var runtimeInitialized = false; + function updateMemoryViews() { + var b = wasmMemory.buffer; + HEAP8 = new Int8Array(b); + HEAP16 = new Int16Array(b); + HEAPU8 = new Uint8Array(b); + new Uint16Array(b); + HEAP32 = new Int32Array(b); + HEAPU32 = new Uint32Array(b); + new Float32Array(b); + new Float64Array(b); + HEAP64 = new BigInt64Array(b); + new BigUint64Array(b); + } + function initMemory() { + if (Module["wasmMemory"]) wasmMemory = Module["wasmMemory"]; + else { + var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 8388608; + /** @suppress {checkTypes} */ + wasmMemory = new WebAssembly.Memory({ + "initial": INITIAL_MEMORY / 65536, + "maximum": 32768 + }); + } + updateMemoryViews(); + } + function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; + while (Module["preRun"].length) addOnPreRun(Module["preRun"].shift()); + } + callRuntimeCallbacks(onPreRuns); + } + function initRuntime() { + runtimeInitialized = true; + if (!Module["noFSInit"] && !FS.initialized) FS.init(); + TTY.init(); + wasmExports["__wasm_call_ctors"](); + FS.ignorePermissions = false; + } + function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; + while (Module["postRun"].length) addOnPostRun(Module["postRun"].shift()); + } + callRuntimeCallbacks(onPostRuns); + } + /** @param {string|number=} what */ + function abort(what) { + Module["onAbort"]?.(what); + what = "Aborted(" + what + ")"; + err(what); + ABORT = true; + what += ". Build with -sASSERTIONS for more info."; + /** @suppress {checkTypes} */ + var e = new WebAssembly.RuntimeError(what); + readyPromiseReject?.(e); + throw e; + } + var wasmBinaryFile; + function findWasmBinary() { + if (Module["locateFile"]) return locateFile("sqlite3.wasm"); + return new URL("sqlite3.wasm", import.meta.url).href; + } + 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"; + } + async function getWasmBinary(binaryFile) { + if (!wasmBinary) try { + var response = await readAsync(binaryFile); + return new Uint8Array(response); + } catch {} + return getBinarySync(binaryFile); + } + async function instantiateArrayBuffer(binaryFile, imports) { + try { + var binary = await getWasmBinary(binaryFile); + return await WebAssembly.instantiate(binary, imports); + } catch (reason) { + err(`failed to asynchronously prepare wasm: ${reason}`); + abort(reason); + } + } + async function instantiateAsync(binary, binaryFile, imports) { + if (!binary) try { + var response = fetch(binaryFile, { credentials: "same-origin" }); + return await WebAssembly.instantiateStreaming(response, imports); + } catch (reason) { + err(`wasm streaming compile failed: ${reason}`); + err("falling back to ArrayBuffer instantiation"); + } + return instantiateArrayBuffer(binaryFile, imports); + } + function getWasmImports() { + return { + "env": wasmImports, + "wasi_snapshot_preview1": wasmImports + }; + } + async function createWasm() { + /** @param {WebAssembly.Module=} module*/ + function receiveInstance(instance, module) { + wasmExports = instance.exports; + assignWasmExports(wasmExports); + return wasmExports; + } + function receiveInstantiationResult(result) { + return receiveInstance(result["instance"]); + } + var info = getWasmImports(); + if (Module["instantiateWasm"]) return new Promise((resolve, reject) => { + Module["instantiateWasm"](info, (inst, mod) => { + resolve(receiveInstance(inst, mod)); + }); + }); + wasmBinaryFile ??= findWasmBinary(); + return receiveInstantiationResult(await instantiateAsync(wasmBinary, wasmBinaryFile, info)); + } + var callRuntimeCallbacks = (callbacks) => { + while (callbacks.length > 0) callbacks.shift()(Module); + }; + var onPostRuns = []; + var addOnPostRun = (cb) => onPostRuns.push(cb); + var onPreRuns = []; + var addOnPreRun = (cb) => onPreRuns.push(cb); + var wasmMemory; + var PATH = { + isAbs: (path) => path.charAt(0) === "/", + splitPath: (filename) => { + return /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.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.slice(-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.slice(0, -1); + return root + dir; + }, + basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1], + join: (...paths) => PATH.normalize(paths.join("/")), + join2: (l, r) => PATH.normalize(l + "/" + r) + }; + var initRandomFill = () => { + return (view) => crypto.getRandomValues(view); + }; + 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).slice(1); + to = PATH_FS.resolve(to).slice(1); + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) if (arr[start] !== "") break; + var end = arr.length - 1; + for (; end >= 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 < length; i++) if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) outputParts.push(".."); + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join("/"); + } + }; + var UTF8Decoder = new TextDecoder(); + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; + return idx; + }; + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number=} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { + var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); + return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr))); + }; + var FS_stdin_getChar_buffer = []; + var lengthBytesUTF8 = (str) => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + var c = str.charCodeAt(i); + if (c <= 127) len++; + else if (c <= 2047) len += 2; + else if (c >= 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 < str.length; ++i) { + var u = str.codePointAt(i); + 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; + i++; + } + } + heap[outIdx] = 0; + return outIdx - startIdx; + }; + /** @type {function(string, boolean=, number=)} */ + var 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 (globalThis.window?.prompt) { + result = window.prompt("Input: "); + if (result !== null) result += "\n"; + } + 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; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); + if (result === null || result === void 0) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) stream.node.atime = Date.now(); + return bytesRead; + }, + write(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) throw new FS.ErrnoError(60); + try { + for (var i = 0; i < length; i++) stream.tty.ops.put_char(stream.tty, buffer[offset + i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) stream.node.mtime = stream.node.ctime = Date.now(); + return i; + } + }, + default_tty_ops: { + get_char(tty) { + return FS_stdin_getChar(); + }, + put_char(tty, val) { + if (val === null || val === 10) { + out(UTF8ArrayToString(tty.output)); + tty.output = []; + } else if (val != 0) tty.output.push(val); + }, + fsync(tty) { + if (tty.output?.length > 0) { + out(UTF8ArrayToString(tty.output)); + 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)); + tty.output = []; + } else if (val != 0) tty.output.push(val); + }, + fsync(tty) { + if (tty.output?.length > 0) { + err(UTF8ArrayToString(tty.output)); + tty.output = []; + } + } + } + }; + var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); + var alignMemory = (size, alignment) => { + return Math.ceil(size / alignment) * alignment; + }; + var mmapAlloc = (size) => { + size = alignMemory(size, 65536); + var ptr = _emscripten_builtin_memalign(65536, size); + if (ptr) zeroMemory(ptr, size); + return ptr; + }; + var MEMFS = { + ops_table: null, + mount(mount) { + return MEMFS.createNode(null, "/", 16895, 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, + 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.atime = node.mtime = node.ctime = Date.now(); + if (parent) { + parent.contents[name] = node; + parent.atime = parent.mtime = parent.ctime = node.atime; + } + 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; + newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 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.atime); + attr.mtime = new Date(node.mtime); + attr.ctime = new Date(node.ctime); + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr(node, attr) { + for (const key of [ + "mode", + "atime", + "mtime", + "ctime" + ]) if (attr[key] != null) node[key] = attr[key]; + if (attr.size !== void 0) MEMFS.resizeFileStorage(node, attr.size); + }, + lookup(parent, name) { + if (!MEMFS.doesNotExistError) { + MEMFS.doesNotExistError = new FS.ErrnoError(44); + /** @suppress {checkTypes} */ + MEMFS.doesNotExistError.stack = ""; + } + throw MEMFS.doesNotExistError; + }, + mknod(parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + }, + rename(old_node, new_dir, new_name) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (new_node) { + if (FS.isDir(old_node.mode)) for (var i in new_node.contents) throw new FS.ErrnoError(55); + FS.hashRemoveNode(new_node); + } + delete old_node.parent.contents[old_node.name]; + new_dir.contents[new_name] = old_node; + old_node.name = new_name; + new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); + }, + unlink(parent, name) { + delete parent.contents[name]; + parent.ctime = parent.mtime = Date.now(); + }, + rmdir(parent, name) { + for (var i in FS.lookupNode(parent, name).contents) throw new FS.ErrnoError(55); + delete parent.contents[name]; + parent.ctime = parent.mtime = Date.now(); + }, + readdir(node) { + return [ + ".", + "..", + ...Object.keys(node.contents) + ]; + }, + symlink(parent, newname, oldpath) { + var node = MEMFS.createNode(parent, newname, 41471, 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; i < size; i++) buffer[offset + i] = contents[position + i]; + return size; + }, + write(stream, buffer, offset, length, position, canOwn) { + if (buffer.buffer === HEAP8.buffer) canOwn = false; + if (!length) return 0; + var node = stream.node; + node.mtime = node.ctime = Date.now(); + if (buffer.subarray && (!node.contents || node.contents.subarray)) { + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + return length; + } else if (node.usedBytes === 0 && position === 0) { + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + return length; + } else if (position + length <= node.usedBytes) { + node.contents.set(buffer.subarray(offset, offset + length), position); + return length; + } + } + MEMFS.expandFileStorage(node, position + length); + if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); + else for (var i = 0; i < length; i++) node.contents[position + i] = buffer[offset + i]; + node.usedBytes = Math.max(node.usedBytes, position + length); + return length; + }, + llseek(stream, offset, whence) { + var position = offset; + if (whence === 1) position += stream.position; + else if (whence === 2) { + if (FS.isFile(stream.node.mode)) position += stream.node.usedBytes; + } + if (position < 0) throw new FS.ErrnoError(28); + return position; + }, + mmap(stream, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43); + var ptr; + var allocated; + var contents = stream.node.contents; + if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { + allocated = false; + ptr = contents.byteOffset; + } else { + allocated = true; + ptr = mmapAlloc(length); + if (!ptr) throw new FS.ErrnoError(48); + if (contents) { + if (position > 0 || position + length < contents.length) if (contents.subarray) contents = contents.subarray(position, position + length); + else contents = Array.prototype.slice.call(contents, position, position + length); + HEAP8.set(contents, ptr); + } + } + return { + ptr, + allocated + }; + }, + msync(stream, buffer, offset, length, mmapFlags) { + MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); + return 0; + } + } + }; + var FS_modeStringToFlags = (str) => { + var flags = { + "r": 0, + "r+": 2, + "w": 577, + "w+": 578, + "a": 1089, + "a+": 1090 + }[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 |= 365; + if (canWrite) mode |= 146; + return mode; + }; + var asyncLoad = async (url) => { + var arrayBuffer = await readAsync(url); + return new Uint8Array(arrayBuffer); + }; + var FS_createDataFile = (...args) => FS.createDataFile(...args); + var getUniqueRunDependency = (id) => { + return id; + }; + var runDependencies = 0; + var dependenciesFulfilled = null; + var removeRunDependency = (id) => { + runDependencies--; + Module["monitorRunDependencies"]?.(runDependencies); + if (runDependencies == 0) { + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); + } + } + }; + var addRunDependency = (id) => { + runDependencies++; + Module["monitorRunDependencies"]?.(runDependencies); + }; + var preloadPlugins = []; + var FS_handledByPreloadPlugin = async (byteArray, fullname) => { + if (typeof Browser != "undefined") Browser.init(); + for (var plugin of preloadPlugins) if (plugin["canHandle"](fullname)) return plugin["handle"](byteArray, fullname); + return byteArray; + }; + var FS_preloadFile = async (parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish) => { + var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; + var dep = getUniqueRunDependency(`cp ${fullname}`); + addRunDependency(dep); + try { + var byteArray = url; + if (typeof url == "string") byteArray = await asyncLoad(url); + byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); + preFinish?.(); + if (!dontCreateFile) FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); + } finally { + removeRunDependency(dep); + } + }; + var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { + FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror); + }; + var FS = { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: "/", + initialized: false, + ignorePermissions: true, + filesystems: null, + syncFSRequests: 0, + readFiles: {}, + ErrnoError: class { + name = "ErrnoError"; + constructor(errno) { + this.errno = errno; + } + }, + FSStream: class { + 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 { + node_ops = {}; + stream_ops = {}; + readMode = 365; + writeMode = 146; + mounted = null; + constructor(parent, name, mode, rdev) { + if (!parent) parent = this; + this.parent = parent; + this.mount = parent.mount; + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.rdev = rdev; + this.atime = this.mtime = this.ctime = Date.now(); + } + 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 = {}) { + if (!path) throw new FS.ErrnoError(44); + opts.follow_mount ??= true; + if (!PATH.isAbs(path)) path = FS.cwd() + "/" + path; + linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { + var parts = path.split("/").filter((p) => !!p); + var current = FS.root; + var current_path = "/"; + for (var i = 0; i < parts.length; i++) { + var islast = i === parts.length - 1; + if (islast && opts.parent) break; + if (parts[i] === ".") continue; + if (parts[i] === "..") { + current_path = PATH.dirname(current_path); + if (FS.isRoot(current)) { + path = current_path + "/" + parts.slice(i + 1).join("/"); + nlinks--; + continue linkloop; + } else current = current.parent; + continue; + } + current_path = PATH.join2(current_path, parts[i]); + try { + current = FS.lookupNode(current, parts[i]); + } catch (e) { + if (e?.errno === 44 && islast && opts.noent_okay) return { path: current_path }; + throw e; + } + if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) current = current.mounted.root; + if (FS.isLink(current.mode) && (!islast || opts.follow)) { + if (!current.node_ops.readlink) throw new FS.ErrnoError(52); + var link = current.node_ops.readlink(current); + if (!PATH.isAbs(link)) link = PATH.dirname(current_path) + "/" + link; + path = link + "/" + parts.slice(i + 1).join("/"); + continue linkloop; + } + } + return { + path: current_path, + node: current + }; + } + throw new FS.ErrnoError(32); + }, + 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 < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0; + return (parentid + hash >>> 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) { + if (!FS.isDir(dir.mode)) return 54; + try { + 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 & 576) return 31; + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + }, + checkOpExists(op, err) { + if (!op) throw new FS.ErrnoError(err); + return op; + }, + 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; + }, + doSetAttr(stream, node, attr) { + var setattr = stream?.stream_ops.setattr; + var arg = setattr ? stream : node; + setattr ??= node.node_ops.setattr; + FS.checkOpExists(setattr, 63); + setattr(arg, attr); + }, + chrdev_stream_ops: { + open(stream) { + stream.stream_ops = FS.getDevice(stream.node.rdev).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); + } + for (var mount of mounts) if (mount.type.syncfs) mount.type.syncfs(mount, populate, done); + else done(null); + }, + 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); + for (var [hash, current] of Object.entries(FS.nameTable)) 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 parent = FS.lookupPath(path, { parent: true }).node; + var name = PATH.basename(path); + if (!name) throw new FS.ErrnoError(28); + if (name === "." || name === "..") throw new FS.ErrnoError(20); + 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); + }, + statfs(path) { + return FS.statfsNode(FS.lookupPath(path, { follow: true }).node); + }, + statfsStream(stream) { + return FS.statfsNode(stream.node); + }, + statfsNode(node) { + var rtn = { + bsize: 4096, + frsize: 4096, + blocks: 1e6, + bfree: 5e5, + bavail: 5e5, + files: FS.nextInode, + ffree: FS.nextInode - 1, + fsid: 42, + flags: 2, + namelen: 255 + }; + if (node.node_ops.statfs) Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root)); + return rtn; + }, + create(path, mode = 438) { + mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + }, + mkdir(path, mode = 511) { + mode &= 1023; + mode |= 16384; + return FS.mknod(path, mode, 0); + }, + mkdirTree(path, mode) { + var dirs = path.split("/"); + var d = ""; + for (var dir of dirs) { + if (!dir) continue; + if (d || PATH.isAbs(path)) d += "/"; + d += dir; + try { + FS.mkdir(d, mode); + } catch (e) { + if (e.errno != 20) throw e; + } + } + }, + mkdev(path, mode, dev) { + if (typeof dev == "undefined") { + dev = mode; + mode = 438; + } + mode |= 8192; + return FS.mknod(path, mode, dev); + }, + symlink(oldpath, newpath) { + if (!PATH_FS.resolve(oldpath)) throw new FS.ErrnoError(44); + var parent = FS.lookupPath(newpath, { parent: true }).node; + if (!parent) throw new FS.ErrnoError(44); + var newname = PATH.basename(newpath); + var errCode = FS.mayCreate(parent, newname); + if (errCode) throw new FS.ErrnoError(errCode); + if (!parent.node_ops.symlink) throw new FS.ErrnoError(63); + return parent.node_ops.symlink(parent, newname, oldpath); + }, + rename(old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + var lookup = FS.lookupPath(old_path, { parent: true }), old_dir = lookup.node, new_dir; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + if (!old_dir || !new_dir) throw new FS.ErrnoError(44); + if (old_dir.mount !== new_dir.mount) throw new FS.ErrnoError(75); + var old_node = FS.lookupNode(old_dir, old_name); + var relative = PATH_FS.relative(old_path, new_dirname); + if (relative.charAt(0) !== ".") throw new FS.ErrnoError(28); + relative = PATH_FS.relative(new_path, old_dirname); + if (relative.charAt(0) !== ".") throw new FS.ErrnoError(55); + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (old_node === new_node) return; + var isdir = FS.isDir(old_node.mode); + var errCode = FS.mayDelete(old_dir, old_name, isdir); + if (errCode) throw new FS.ErrnoError(errCode); + errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); + if (errCode) throw new FS.ErrnoError(errCode); + if (!old_dir.node_ops.rename) throw new FS.ErrnoError(63); + if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) throw new FS.ErrnoError(10); + if (new_dir !== old_dir) { + errCode = FS.nodePermissions(old_dir, "w"); + if (errCode) throw new FS.ErrnoError(errCode); + } + FS.hashRemoveNode(old_node); + try { + old_dir.node_ops.rename(old_node, new_dir, new_name); + old_node.parent = new_dir; + } catch (e) { + throw e; + } finally { + FS.hashAddNode(old_node); + } + }, + rmdir(path) { + var parent = FS.lookupPath(path, { parent: true }).node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, true); + if (errCode) throw new FS.ErrnoError(errCode); + if (!parent.node_ops.rmdir) throw new FS.ErrnoError(63); + if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + }, + readdir(path) { + var node = FS.lookupPath(path, { follow: true }).node; + return FS.checkOpExists(node.node_ops.readdir, 54)(node); + }, + unlink(path) { + var parent = FS.lookupPath(path, { parent: true }).node; + if (!parent) throw new FS.ErrnoError(44); + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var errCode = FS.mayDelete(parent, name, false); + if (errCode) throw new FS.ErrnoError(errCode); + if (!parent.node_ops.unlink) throw new FS.ErrnoError(63); + if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + }, + readlink(path) { + var link = FS.lookupPath(path).node; + if (!link) throw new FS.ErrnoError(44); + if (!link.node_ops.readlink) throw new FS.ErrnoError(28); + return link.node_ops.readlink(link); + }, + stat(path, dontFollow) { + var node = FS.lookupPath(path, { follow: !dontFollow }).node; + return FS.checkOpExists(node.node_ops.getattr, 63)(node); + }, + fstat(fd) { + var stream = FS.getStreamChecked(fd); + var node = stream.node; + var getattr = stream.stream_ops.getattr; + var arg = getattr ? stream : node; + getattr ??= node.node_ops.getattr; + FS.checkOpExists(getattr, 63); + return getattr(arg); + }, + lstat(path) { + return FS.stat(path, true); + }, + doChmod(stream, node, mode, dontFollow) { + FS.doSetAttr(stream, node, { + mode: mode & 4095 | node.mode & -4096, + ctime: Date.now(), + dontFollow + }); + }, + chmod(path, mode, dontFollow) { + var node; + if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; + else node = path; + FS.doChmod(null, node, mode, dontFollow); + }, + lchmod(path, mode) { + FS.chmod(path, mode, true); + }, + fchmod(fd, mode) { + var stream = FS.getStreamChecked(fd); + FS.doChmod(stream, stream.node, mode, false); + }, + doChown(stream, node, dontFollow) { + FS.doSetAttr(stream, node, { + timestamp: Date.now(), + dontFollow + }); + }, + chown(path, uid, gid, dontFollow) { + var node; + if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; + else node = path; + FS.doChown(null, node, dontFollow); + }, + lchown(path, uid, gid) { + FS.chown(path, uid, gid, true); + }, + fchown(fd, uid, gid) { + var stream = FS.getStreamChecked(fd); + FS.doChown(stream, stream.node, false); + }, + doTruncate(stream, node, len) { + if (FS.isDir(node.mode)) throw new FS.ErrnoError(31); + if (!FS.isFile(node.mode)) throw new FS.ErrnoError(28); + var errCode = FS.nodePermissions(node, "w"); + if (errCode) throw new FS.ErrnoError(errCode); + FS.doSetAttr(stream, node, { + size: len, + timestamp: Date.now() + }); + }, + truncate(path, len) { + if (len < 0) throw new FS.ErrnoError(28); + var node; + if (typeof path == "string") node = FS.lookupPath(path, { follow: true }).node; + else node = path; + FS.doTruncate(null, node, len); + }, + ftruncate(fd, len) { + var stream = FS.getStreamChecked(fd); + if (len < 0 || (stream.flags & 2097155) === 0) throw new FS.ErrnoError(28); + FS.doTruncate(stream, stream.node, len); + }, + utime(path, atime, mtime) { + var node = FS.lookupPath(path, { follow: true }).node; + FS.checkOpExists(node.node_ops.setattr, 63)(node, { + atime, + mtime + }); + }, + open(path, flags, mode = 438) { + if (path === "") throw new FS.ErrnoError(44); + flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; + if (flags & 64) mode = mode & 4095 | 32768; + else mode = 0; + var node; + var isDirPath; + if (typeof path == "object") node = path; + else { + isDirPath = path.endsWith("/"); + var lookup = FS.lookupPath(path, { + follow: !(flags & 131072), + noent_okay: true + }); + node = lookup.node; + path = lookup.path; + } + var created = false; + if (flags & 64) if (node) { + if (flags & 128) throw new FS.ErrnoError(20); + } else if (isDirPath) throw new FS.ErrnoError(31); + else { + node = FS.mknod(path, mode | 511, 0); + created = true; + } + if (!node) throw new FS.ErrnoError(44); + if (FS.isChrdev(node.mode)) flags &= -513; + if (flags & 65536 && !FS.isDir(node.mode)) throw new FS.ErrnoError(54); + if (!created) { + var errCode = FS.mayOpen(node, flags); + if (errCode) throw new FS.ErrnoError(errCode); + } + if (flags & 512 && !created) FS.truncate(node, 0); + flags &= -131713; + var stream = FS.createStream({ + node, + path: FS.getPath(node), + flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + ungotten: [], + error: false + }); + if (stream.stream_ops.open) stream.stream_ops.open(stream); + if (created) FS.chmod(node, mode & 511); + if (Module["logReadFiles"] && !(flags & 1)) { + if (!(path in FS.readFiles)) FS.readFiles[path] = 1; + } + return stream; + }, + close(stream) { + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if (stream.getdents) stream.getdents = null; + try { + if (stream.stream_ops.close) stream.stream_ops.close(stream); + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + stream.fd = null; + }, + isClosed(stream) { + return stream.fd === null; + }, + llseek(stream, offset, whence) { + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if (!stream.seekable || !stream.stream_ops.llseek) throw new FS.ErrnoError(70); + if (whence != 0 && whence != 1 && whence != 2) throw new FS.ErrnoError(28); + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; + }, + read(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) throw new FS.ErrnoError(28); + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(8); + if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); + if (!stream.stream_ops.read) throw new FS.ErrnoError(28); + var seeking = typeof position != "undefined"; + if (!seeking) position = stream.position; + else if (!stream.seekable) throw new FS.ErrnoError(70); + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write(stream, buffer, offset, length, position, canOwn) { + if (length < 0 || position < 0) throw new FS.ErrnoError(28); + if (FS.isClosed(stream)) throw new FS.ErrnoError(8); + if ((stream.flags & 2097155) === 0) throw new FS.ErrnoError(8); + if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); + if (!stream.stream_ops.write) throw new FS.ErrnoError(28); + if (stream.seekable && stream.flags & 1024) FS.llseek(stream, 0, 2); + var seeking = typeof position != "undefined"; + if (!seeking) position = stream.position; + else if (!stream.seekable) throw new FS.ErrnoError(70); + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); + if (!seeking) stream.position += bytesWritten; + return bytesWritten; + }, + mmap(stream, length, position, prot, flags) { + if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) throw new FS.ErrnoError(2); + if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(2); + if (!stream.stream_ops.mmap) throw new FS.ErrnoError(43); + if (!length) throw new FS.ErrnoError(28); + return stream.stream_ops.mmap(stream, length, position, prot, flags); + }, + msync(stream, buffer, offset, length, mmapFlags) { + if (!stream.stream_ops.msync) return 0; + return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); + }, + ioctl(stream, cmd, arg) { + if (!stream.stream_ops.ioctl) throw new FS.ErrnoError(59); + return stream.stream_ops.ioctl(stream, cmd, arg); + }, + readFile(path, opts = {}) { + opts.flags = opts.flags || 0; + opts.encoding = opts.encoding || "binary"; + if (opts.encoding !== "utf8" && opts.encoding !== "binary") abort(`Invalid encoding type "${opts.encoding}"`); + var stream = FS.open(path, opts.flags); + var length = FS.stat(path).size; + var buf = new Uint8Array(length); + FS.read(stream, buf, 0, length, 0); + if (opts.encoding === "utf8") buf = UTF8ArrayToString(buf); + FS.close(stream); + return buf; + }, + writeFile(path, data, opts = {}) { + opts.flags = opts.flags || 577; + var stream = FS.open(path, opts.flags, opts.mode); + if (typeof data == "string") data = new Uint8Array(intArrayFromString(data, true)); + if (ArrayBuffer.isView(data)) FS.write(stream, data, 0, data.byteLength, void 0, opts.canOwn); + else abort("Unsupported data type"); + FS.close(stream); + }, + cwd: () => FS.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, + llseek: () => 0 + }); + 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) { + randomFill(randomBuffer); + randomLeft = 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", 16895, 73); + node.stream_ops = { llseek: MEMFS.stream_ops.llseek }; + 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 }, + id: fd + 1 + }; + ret.parent = ret; + return ret; + }, + readdir() { + return Array.from(FS.streams.entries()).filter(([k, v]) => v).map(([k, v]) => k.toString()); + } + }; + 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"); + FS.open("/dev/stdin", 0); + FS.open("/dev/stdout", 1); + FS.open("/dev/stderr", 1); + }, + staticInit() { + FS.nameTable = new Array(4096); + FS.mount(MEMFS, {}, "/"); + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + FS.filesystems = { "MEMFS": 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 stream of FS.streams) if (stream) FS.close(stream); + }, + findObject(path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (!ret.exists) return null; + return ret.object; + }, + analyzePath(path, dontResolveLastLink) { + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) {} + var ret = { + isRoot: false, + exists: false, + error: 0, + name: null, + path: null, + object: null, + parentExists: false, + parentPath: null, + parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === "/"; + } catch (e) { + ret.error = e.errno; + } + return ret; + }, + createPath(parent, path, canRead, canWrite) { + parent = typeof parent == "string" ? parent : FS.getPath(parent); + var parts = path.split("/").reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join2(parent, part); + try { + FS.mkdir(current); + } catch (e) { + if (e.errno != 20) throw e; + } + parent = current; + } + return current; + }, + createFile(parent, name, properties, canRead, canWrite) { + var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); + var mode = FS_getMode(canRead, canWrite); + return FS.create(path, mode); + }, + createDataFile(parent, name, data, canRead, canWrite, canOwn) { + var path = name; + if (parent) { + parent = typeof parent == "string" ? parent : FS.getPath(parent); + path = name ? PATH.join2(parent, name) : parent; + } + var mode = FS_getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data == "string") { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + FS.chmod(node, mode | 146); + var stream = FS.open(node, 577); + FS.write(stream, data, 0, data.length, 0, canOwn); + FS.close(stream); + FS.chmod(node, mode); + } + }, + createDevice(parent, name, input, output) { + var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); + var mode = FS_getMode(!!input, !!output); + FS.createDevice.major ??= 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + FS.registerDevice(dev, { + open(stream) { + stream.seekable = false; + }, + close(stream) { + if (output?.buffer?.length) output(10); + }, + read(stream, buffer, offset, length, pos) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); + if (result === null || result === void 0) break; + bytesRead++; + buffer[offset + i] = result; + } + if (bytesRead) stream.node.atime = Date.now(); + return bytesRead; + }, + write(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) try { + output(buffer[offset + i]); + } catch (e) { + throw new FS.ErrnoError(29); + } + if (length) stream.node.mtime = stream.node.ctime = Date.now(); + return i; + } + }); + return FS.mkdev(path, mode, dev); + }, + forceLoadFile(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + if (globalThis.XMLHttpRequest) abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + else try { + obj.contents = readBinary(obj.url); + } catch (e) { + throw new FS.ErrnoError(29); + } + }, + createLazyFile(parent, name, url, canRead, canWrite) { + class LazyUint8Array { + lengthKnown = false; + chunks = []; + get(idx) { + if (idx > this.length - 1 || idx < 0) return; + 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)) abort("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) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength - 1) abort("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)) abort("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== void 0) 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") abort("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 (globalThis.XMLHttpRequest) { + if (!ENVIRONMENT_IS_WORKER) abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"); + var properties = { + isDevice: false, + contents: new LazyUint8Array() + }; + } 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 = {}; + for (const [key, fn] of Object.entries(node.stream_ops)) 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 < size; i++) buffer[offset + i] = contents[position + i]; + else for (var i = 0; i < size; i++) buffer[offset + i] = contents.get(position + i); + return size; + } + stream_ops.read = (stream, buffer, offset, length, position) => { + 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; + } + }; + /** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index. + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { + if (!ptr) return ""; + var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); + return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); + }; + var SYSCALLS = { + calculateAt(dirfd, path, allowEmpty) { + if (PATH.isAbs(path)) return path; + var dir; + if (dirfd === -100) dir = FS.cwd(); + else dir = SYSCALLS.getStreamFromFD(dirfd).path; + if (path.length == 0) { + if (!allowEmpty) throw new FS.ErrnoError(44); + return dir; + } + return dir + "/" + path; + }, + writeStat(buf, stat) { + HEAPU32[buf >> 2] = stat.dev; + HEAPU32[buf + 4 >> 2] = stat.mode; + HEAPU32[buf + 8 >> 2] = stat.nlink; + HEAPU32[buf + 12 >> 2] = stat.uid; + HEAPU32[buf + 16 >> 2] = stat.gid; + HEAPU32[buf + 20 >> 2] = stat.rdev; + HEAP64[buf + 24 >> 3] = BigInt(stat.size); + 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(); + HEAP64[buf + 40 >> 3] = BigInt(Math.floor(atime / 1e3)); + HEAPU32[buf + 48 >> 2] = atime % 1e3 * 1e3 * 1e3; + HEAP64[buf + 56 >> 3] = BigInt(Math.floor(mtime / 1e3)); + HEAPU32[buf + 64 >> 2] = mtime % 1e3 * 1e3 * 1e3; + HEAP64[buf + 72 >> 3] = BigInt(Math.floor(ctime / 1e3)); + HEAPU32[buf + 80 >> 2] = ctime % 1e3 * 1e3 * 1e3; + HEAP64[buf + 88 >> 3] = BigInt(stat.ino); + return 0; + }, + writeStatFs(buf, stats) { + HEAPU32[buf + 4 >> 2] = stats.bsize; + HEAPU32[buf + 60 >> 2] = stats.bsize; + HEAP64[buf + 8 >> 3] = BigInt(stats.blocks); + HEAP64[buf + 16 >> 3] = BigInt(stats.bfree); + HEAP64[buf + 24 >> 3] = BigInt(stats.bavail); + HEAP64[buf + 32 >> 3] = BigInt(stats.files); + HEAP64[buf + 40 >> 3] = BigInt(stats.ffree); + HEAPU32[buf + 48 >> 2] = stats.fsid; + HEAPU32[buf + 64 >> 2] = stats.flags; + HEAPU32[buf + 56 >> 2] = stats.namelen; + }, + 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) { + return FS.getStreamChecked(fd); + }, + varargs: void 0, + getStr(ptr) { + return UTF8ToString(ptr); + } + }; + 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 & -8) return -28; + var node = FS.lookupPath(path, { follow: true }).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; + } + } + var 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++; + return FS.dupStream(stream, arg).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 { + return SYSCALLS.writeStat(buf, FS.fstat(fd)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + var INT53_MAX = 9007199254740992; + var INT53_MIN = -9007199254740992; + var bigintToI53Checked = (num) => num < INT53_MIN || num > INT53_MAX ? NaN : Number(num); + function ___syscall_ftruncate64(fd, length) { + length = bigintToI53Checked(length); + 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) => { + return 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 (size < cwdLengthInBytes) return -68; + stringToUTF8(cwd, buf, size); + return cwdLengthInBytes; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_ioctl(fd, op, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (op) { + case 21509: + if (!stream.tty) return -59; + return 0; + case 21505: + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcgets) { + var termios = stream.tty.ops.ioctl_tcgets(stream); + var argp = syscallGetVarargP(); + HEAP32[argp >> 2] = termios.c_iflag || 0; + HEAP32[argp + 4 >> 2] = termios.c_oflag || 0; + HEAP32[argp + 8 >> 2] = termios.c_cflag || 0; + HEAP32[argp + 12 >> 2] = termios.c_lflag || 0; + for (var i = 0; i < 32; i++) HEAP8[argp + i + 17] = termios.c_cc[i] || 0; + return 0; + } + return 0; + case 21510: + case 21511: + case 21512: + if (!stream.tty) return -59; + return 0; + case 21506: + case 21507: + case 21508: + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcsets) { + var argp = syscallGetVarargP(); + var c_iflag = HEAP32[argp >> 2]; + var c_oflag = HEAP32[argp + 4 >> 2]; + var c_cflag = HEAP32[argp + 8 >> 2]; + var c_lflag = HEAP32[argp + 12 >> 2]; + var c_cc = []; + for (var i = 0; i < 32; i++) c_cc.push(HEAP8[argp + i + 17]); + return stream.tty.ops.ioctl_tcsets(stream.tty, op, { + c_iflag, + c_oflag, + c_cflag, + c_lflag, + c_cc + }); + } + return 0; + case 21519: + if (!stream.tty) return -59; + var argp = syscallGetVarargP(); + HEAP32[argp >> 2] = 0; + return 0; + case 21520: + if (!stream.tty) return -59; + return -28; + case 21537: + case 21531: + var argp = syscallGetVarargP(); + return FS.ioctl(stream, op, argp); + case 21523: + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tiocgwinsz) { + var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); + var argp = syscallGetVarargP(); + HEAP16[argp >> 1] = winsize[0]; + HEAP16[argp + 2 >> 1] = winsize[1]; + } + return 0; + case 21524: + if (!stream.tty) return -59; + return 0; + case 21515: + if (!stream.tty) return -59; + return 0; + default: return -28; + } + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_lstat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.writeStat(buf, FS.lstat(path)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_mkdirat(dirfd, path, mode) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + FS.mkdir(path, mode, 0); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_newfstatat(dirfd, path, buf, flags) { + try { + path = SYSCALLS.getStr(path); + var nofollow = flags & 256; + var allowEmpty = flags & 4096; + flags = flags & -6401; + path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); + return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_openat(dirfd, path, flags, varargs) { + SYSCALLS.varargs = varargs; + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + var mode = varargs ? syscallGetVarargI() : 0; + return FS.open(path, flags, mode).fd; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_readlinkat(dirfd, path, buf, bufsize) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (bufsize <= 0) return -28; + var ret = FS.readlink(path); + var len = Math.min(bufsize, lengthBytesUTF8(ret)); + var endChar = HEAP8[buf + len]; + stringToUTF8(ret, buf, bufsize + 1); + HEAP8[buf + len] = endChar; + return len; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_rmdir(path) { + try { + path = SYSCALLS.getStr(path); + FS.rmdir(path); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_stat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.writeStat(buf, FS.stat(path)); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + function ___syscall_unlinkat(dirfd, path, flags) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (!flags) FS.unlink(path); + else if (flags === 512) FS.rmdir(path); + else return -28; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + var readI53FromI64 = (ptr) => { + return HEAPU32[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 = null; + 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 = null; + else mtime = seconds * 1e3 + nanoseconds / (1e3 * 1e3); + } + if ((mtime ?? atime) !== null) FS.utime(path, atime, mtime); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return -e.errno; + } + } + 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) => { + return (isLeapYear(date.getFullYear()) ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE)[date.getMonth()] + date.getDate() - 1; + }; + function __localtime_js(time, tmPtr) { + time = bigintToI53Checked(time); + var date = /* @__PURE__ */ 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, allocated, addr) { + offset = bigintToI53Checked(offset); + try { + 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) { + offset = bigintToI53Checked(offset); + 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 __tzset_js = (timezone, daylight, std_name, dst_name) => { + var currentYear = (/* @__PURE__ */ 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); + return `UTC${sign}${String(Math.floor(absOffset / 60)).padStart(2, "0")}${String(absOffset % 60).padStart(2, "0")}`; + }; + var winterName = extractZone(winterOffset); + var summerName = extractZone(summerOffset); + if (summerOffset < winterOffset) { + stringToUTF8(winterName, std_name, 17); + stringToUTF8(summerName, dst_name, 17); + } else { + stringToUTF8(winterName, dst_name, 17); + stringToUTF8(summerName, std_name, 17); + } + }; + var _emscripten_get_now = () => performance.now(); + var _emscripten_date_now = () => Date.now(); + var nowIsMonotonic = 1; + var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + function _clock_time_get(clk_id, ignored_precision, ptime) { + ignored_precision = bigintToI53Checked(ignored_precision); + if (!checkWasiClock(clk_id)) return 28; + var now; + if (clk_id === 0) now = _emscripten_date_now(); + else if (nowIsMonotonic) now = _emscripten_get_now(); + else return 52; + var nsec = Math.round(now * 1e3 * 1e3); + HEAP64[ptime >> 3] = BigInt(nsec); + return 0; + } + var getHeapMax = () => 2147483648; + var _emscripten_get_heap_max = () => getHeapMax(); + var growMemory = (size) => { + var pages = (size - wasmMemory.buffer.byteLength + 65535) / 65536 | 0; + 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); + if (growMemory(Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)))) return true; + } + return false; + }; + var ENV = {}; + var getExecutableName = () => thisProgram || "./this.program"; + var getEnvStrings = () => { + if (!getEnvStrings.strings) { + var lang = (globalThis.navigator?.language ?? "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] === void 0) 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 _environ_get = (__environ, environ_buf) => { + var bufSize = 0; + var envp = 0; + for (var string of getEnvStrings()) { + var ptr = environ_buf + bufSize; + HEAPU32[__environ + envp >> 2] = ptr; + bufSize += stringToUTF8(string, ptr, Infinity) + 1; + envp += 4; + } + return 0; + }; + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { + var strings = getEnvStrings(); + HEAPU32[penviron_count >> 2] = strings.length; + var bufSize = 0; + for (var string of strings) bufSize += lengthBytesUTF8(string) + 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; + HEAP64[pbuf + 8 >> 3] = BigInt(rightsBase); + HEAP64[pbuf + 16 >> 3] = BigInt(rightsInheriting); + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + /** @param {number=} offset */ + var doReadv = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 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 < len) break; + if (typeof offset != "undefined") offset += curr; + } + return ret; + }; + function _fd_read(fd, iov, iovcnt, pnum) { + try { + var num = doReadv(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + function _fd_seek(fd, offset, whence, newOffset) { + offset = bigintToI53Checked(offset); + try { + if (isNaN(offset)) return 61; + var stream = SYSCALLS.getStreamFromFD(fd); + FS.llseek(stream, offset, whence); + HEAP64[newOffset >> 3] = BigInt(stream.position); + 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); + return stream.stream_ops?.fsync?.(stream); + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + /** @param {number=} offset */ + var doWritev = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 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 < len) break; + if (typeof offset != "undefined") offset += curr; + } + return ret; + }; + function _fd_write(fd, iov, iovcnt, pnum) { + try { + var num = doWritev(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; + return e.errno; + } + } + FS.createPreloadedFile = FS_createPreloadedFile; + FS.preloadFile = FS_preloadFile; + FS.staticInit(); + initMemory(); + if (Module["noExitRuntime"]) Module["noExitRuntime"]; + if (Module["preloadPlugins"]) preloadPlugins = Module["preloadPlugins"]; + if (Module["print"]) out = Module["print"]; + if (Module["printErr"]) err = Module["printErr"]; + if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; + if (Module["arguments"]) Module["arguments"]; + if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; + if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; + while (Module["preInit"].length > 0) Module["preInit"].shift()(); + } + Module["wasmMemory"] = wasmMemory; + var _emscripten_builtin_memalign; + function assignWasmExports(wasmExports) { + Module["_sqlite3_status64"] = wasmExports["sqlite3_status64"]; + Module["_sqlite3_status"] = wasmExports["sqlite3_status"]; + Module["_sqlite3_db_status64"] = wasmExports["sqlite3_db_status64"]; + Module["_sqlite3_msize"] = wasmExports["sqlite3_msize"]; + Module["_sqlite3_db_status"] = wasmExports["sqlite3_db_status"]; + Module["_sqlite3_vfs_find"] = wasmExports["sqlite3_vfs_find"]; + Module["_sqlite3_initialize"] = wasmExports["sqlite3_initialize"]; + Module["_sqlite3_malloc"] = wasmExports["sqlite3_malloc"]; + Module["_sqlite3_free"] = wasmExports["sqlite3_free"]; + Module["_sqlite3_vfs_register"] = wasmExports["sqlite3_vfs_register"]; + Module["_sqlite3_vfs_unregister"] = wasmExports["sqlite3_vfs_unregister"]; + Module["_sqlite3_malloc64"] = wasmExports["sqlite3_malloc64"]; + Module["_sqlite3_realloc"] = wasmExports["sqlite3_realloc"]; + Module["_sqlite3_realloc64"] = wasmExports["sqlite3_realloc64"]; + Module["_sqlite3_value_text"] = wasmExports["sqlite3_value_text"]; + Module["_sqlite3_randomness"] = wasmExports["sqlite3_randomness"]; + Module["_sqlite3_stricmp"] = wasmExports["sqlite3_stricmp"]; + Module["_sqlite3_strnicmp"] = wasmExports["sqlite3_strnicmp"]; + Module["_sqlite3_uri_parameter"] = wasmExports["sqlite3_uri_parameter"]; + Module["_sqlite3_uri_boolean"] = wasmExports["sqlite3_uri_boolean"]; + Module["_sqlite3_serialize"] = wasmExports["sqlite3_serialize"]; + Module["_sqlite3_prepare_v2"] = wasmExports["sqlite3_prepare_v2"]; + Module["_sqlite3_step"] = wasmExports["sqlite3_step"]; + Module["_sqlite3_column_int64"] = wasmExports["sqlite3_column_int64"]; + Module["_sqlite3_reset"] = wasmExports["sqlite3_reset"]; + Module["_sqlite3_exec"] = wasmExports["sqlite3_exec"]; + Module["_sqlite3_column_int"] = wasmExports["sqlite3_column_int"]; + Module["_sqlite3_finalize"] = wasmExports["sqlite3_finalize"]; + Module["_sqlite3_file_control"] = wasmExports["sqlite3_file_control"]; + Module["_sqlite3_column_name"] = wasmExports["sqlite3_column_name"]; + Module["_sqlite3_column_text"] = wasmExports["sqlite3_column_text"]; + Module["_sqlite3_column_type"] = wasmExports["sqlite3_column_type"]; + Module["_sqlite3_errmsg"] = wasmExports["sqlite3_errmsg"]; + Module["_sqlite3_deserialize"] = wasmExports["sqlite3_deserialize"]; + Module["_sqlite3_clear_bindings"] = wasmExports["sqlite3_clear_bindings"]; + Module["_sqlite3_value_blob"] = wasmExports["sqlite3_value_blob"]; + Module["_sqlite3_value_bytes"] = wasmExports["sqlite3_value_bytes"]; + Module["_sqlite3_value_double"] = wasmExports["sqlite3_value_double"]; + Module["_sqlite3_value_int"] = wasmExports["sqlite3_value_int"]; + Module["_sqlite3_value_int64"] = wasmExports["sqlite3_value_int64"]; + Module["_sqlite3_value_subtype"] = wasmExports["sqlite3_value_subtype"]; + Module["_sqlite3_value_pointer"] = wasmExports["sqlite3_value_pointer"]; + Module["_sqlite3_value_type"] = wasmExports["sqlite3_value_type"]; + Module["_sqlite3_value_nochange"] = wasmExports["sqlite3_value_nochange"]; + Module["_sqlite3_value_frombind"] = wasmExports["sqlite3_value_frombind"]; + Module["_sqlite3_value_dup"] = wasmExports["sqlite3_value_dup"]; + Module["_sqlite3_value_free"] = wasmExports["sqlite3_value_free"]; + Module["_sqlite3_result_blob"] = wasmExports["sqlite3_result_blob"]; + Module["_sqlite3_result_error_toobig"] = wasmExports["sqlite3_result_error_toobig"]; + Module["_sqlite3_result_error_nomem"] = wasmExports["sqlite3_result_error_nomem"]; + Module["_sqlite3_result_double"] = wasmExports["sqlite3_result_double"]; + Module["_sqlite3_result_error"] = wasmExports["sqlite3_result_error"]; + Module["_sqlite3_result_int"] = wasmExports["sqlite3_result_int"]; + Module["_sqlite3_result_int64"] = wasmExports["sqlite3_result_int64"]; + Module["_sqlite3_result_null"] = wasmExports["sqlite3_result_null"]; + Module["_sqlite3_result_pointer"] = wasmExports["sqlite3_result_pointer"]; + Module["_sqlite3_result_subtype"] = wasmExports["sqlite3_result_subtype"]; + Module["_sqlite3_result_text"] = wasmExports["sqlite3_result_text"]; + Module["_sqlite3_result_zeroblob"] = wasmExports["sqlite3_result_zeroblob"]; + Module["_sqlite3_result_zeroblob64"] = wasmExports["sqlite3_result_zeroblob64"]; + Module["_sqlite3_result_error_code"] = wasmExports["sqlite3_result_error_code"]; + Module["_sqlite3_user_data"] = wasmExports["sqlite3_user_data"]; + Module["_sqlite3_context_db_handle"] = wasmExports["sqlite3_context_db_handle"]; + Module["_sqlite3_vtab_nochange"] = wasmExports["sqlite3_vtab_nochange"]; + Module["_sqlite3_vtab_in_first"] = wasmExports["sqlite3_vtab_in_first"]; + Module["_sqlite3_vtab_in_next"] = wasmExports["sqlite3_vtab_in_next"]; + Module["_sqlite3_aggregate_context"] = wasmExports["sqlite3_aggregate_context"]; + Module["_sqlite3_get_auxdata"] = wasmExports["sqlite3_get_auxdata"]; + Module["_sqlite3_set_auxdata"] = wasmExports["sqlite3_set_auxdata"]; + Module["_sqlite3_column_count"] = wasmExports["sqlite3_column_count"]; + Module["_sqlite3_data_count"] = wasmExports["sqlite3_data_count"]; + Module["_sqlite3_column_blob"] = wasmExports["sqlite3_column_blob"]; + Module["_sqlite3_column_bytes"] = wasmExports["sqlite3_column_bytes"]; + Module["_sqlite3_column_double"] = wasmExports["sqlite3_column_double"]; + Module["_sqlite3_column_value"] = wasmExports["sqlite3_column_value"]; + Module["_sqlite3_column_decltype"] = wasmExports["sqlite3_column_decltype"]; + Module["_sqlite3_column_database_name"] = wasmExports["sqlite3_column_database_name"]; + Module["_sqlite3_column_table_name"] = wasmExports["sqlite3_column_table_name"]; + Module["_sqlite3_column_origin_name"] = wasmExports["sqlite3_column_origin_name"]; + Module["_sqlite3_bind_blob"] = wasmExports["sqlite3_bind_blob"]; + Module["_sqlite3_bind_double"] = wasmExports["sqlite3_bind_double"]; + Module["_sqlite3_bind_int"] = wasmExports["sqlite3_bind_int"]; + Module["_sqlite3_bind_int64"] = wasmExports["sqlite3_bind_int64"]; + Module["_sqlite3_bind_null"] = wasmExports["sqlite3_bind_null"]; + Module["_sqlite3_bind_pointer"] = wasmExports["sqlite3_bind_pointer"]; + Module["_sqlite3_bind_text"] = wasmExports["sqlite3_bind_text"]; + Module["_sqlite3_bind_parameter_count"] = wasmExports["sqlite3_bind_parameter_count"]; + Module["_sqlite3_bind_parameter_name"] = wasmExports["sqlite3_bind_parameter_name"]; + Module["_sqlite3_bind_parameter_index"] = wasmExports["sqlite3_bind_parameter_index"]; + Module["_sqlite3_db_handle"] = wasmExports["sqlite3_db_handle"]; + Module["_sqlite3_stmt_readonly"] = wasmExports["sqlite3_stmt_readonly"]; + Module["_sqlite3_stmt_isexplain"] = wasmExports["sqlite3_stmt_isexplain"]; + Module["_sqlite3_stmt_explain"] = wasmExports["sqlite3_stmt_explain"]; + Module["_sqlite3_stmt_busy"] = wasmExports["sqlite3_stmt_busy"]; + Module["_sqlite3_next_stmt"] = wasmExports["sqlite3_next_stmt"]; + Module["_sqlite3_stmt_status"] = wasmExports["sqlite3_stmt_status"]; + Module["_sqlite3_sql"] = wasmExports["sqlite3_sql"]; + Module["_sqlite3_expanded_sql"] = wasmExports["sqlite3_expanded_sql"]; + Module["_sqlite3_preupdate_old"] = wasmExports["sqlite3_preupdate_old"]; + Module["_sqlite3_preupdate_count"] = wasmExports["sqlite3_preupdate_count"]; + Module["_sqlite3_preupdate_depth"] = wasmExports["sqlite3_preupdate_depth"]; + Module["_sqlite3_preupdate_blobwrite"] = wasmExports["sqlite3_preupdate_blobwrite"]; + Module["_sqlite3_preupdate_new"] = wasmExports["sqlite3_preupdate_new"]; + Module["_sqlite3_value_numeric_type"] = wasmExports["sqlite3_value_numeric_type"]; + Module["_sqlite3_set_authorizer"] = wasmExports["sqlite3_set_authorizer"]; + Module["_sqlite3_strglob"] = wasmExports["sqlite3_strglob"]; + Module["_sqlite3_strlike"] = wasmExports["sqlite3_strlike"]; + Module["_sqlite3_auto_extension"] = wasmExports["sqlite3_auto_extension"]; + Module["_sqlite3_cancel_auto_extension"] = wasmExports["sqlite3_cancel_auto_extension"]; + Module["_sqlite3_reset_auto_extension"] = wasmExports["sqlite3_reset_auto_extension"]; + Module["_sqlite3_prepare_v3"] = wasmExports["sqlite3_prepare_v3"]; + Module["_sqlite3_create_module"] = wasmExports["sqlite3_create_module"]; + Module["_sqlite3_create_module_v2"] = wasmExports["sqlite3_create_module_v2"]; + Module["_sqlite3_drop_modules"] = wasmExports["sqlite3_drop_modules"]; + Module["_sqlite3_declare_vtab"] = wasmExports["sqlite3_declare_vtab"]; + Module["_sqlite3_vtab_on_conflict"] = wasmExports["sqlite3_vtab_on_conflict"]; + Module["_sqlite3_vtab_collation"] = wasmExports["sqlite3_vtab_collation"]; + Module["_sqlite3_vtab_in"] = wasmExports["sqlite3_vtab_in"]; + Module["_sqlite3_vtab_rhs_value"] = wasmExports["sqlite3_vtab_rhs_value"]; + Module["_sqlite3_vtab_distinct"] = wasmExports["sqlite3_vtab_distinct"]; + Module["_sqlite3_keyword_name"] = wasmExports["sqlite3_keyword_name"]; + Module["_sqlite3_keyword_count"] = wasmExports["sqlite3_keyword_count"]; + Module["_sqlite3_keyword_check"] = wasmExports["sqlite3_keyword_check"]; + Module["_sqlite3_complete"] = wasmExports["sqlite3_complete"]; + Module["_sqlite3_libversion"] = wasmExports["sqlite3_libversion"]; + Module["_sqlite3_libversion_number"] = wasmExports["sqlite3_libversion_number"]; + Module["_sqlite3_shutdown"] = wasmExports["sqlite3_shutdown"]; + Module["_sqlite3_last_insert_rowid"] = wasmExports["sqlite3_last_insert_rowid"]; + Module["_sqlite3_set_last_insert_rowid"] = wasmExports["sqlite3_set_last_insert_rowid"]; + Module["_sqlite3_changes64"] = wasmExports["sqlite3_changes64"]; + Module["_sqlite3_changes"] = wasmExports["sqlite3_changes"]; + Module["_sqlite3_total_changes64"] = wasmExports["sqlite3_total_changes64"]; + Module["_sqlite3_total_changes"] = wasmExports["sqlite3_total_changes"]; + Module["_sqlite3_txn_state"] = wasmExports["sqlite3_txn_state"]; + Module["_sqlite3_close_v2"] = wasmExports["sqlite3_close_v2"]; + Module["_sqlite3_busy_handler"] = wasmExports["sqlite3_busy_handler"]; + Module["_sqlite3_progress_handler"] = wasmExports["sqlite3_progress_handler"]; + Module["_sqlite3_busy_timeout"] = wasmExports["sqlite3_busy_timeout"]; + Module["_sqlite3_interrupt"] = wasmExports["sqlite3_interrupt"]; + Module["_sqlite3_is_interrupted"] = wasmExports["sqlite3_is_interrupted"]; + Module["_sqlite3_create_function"] = wasmExports["sqlite3_create_function"]; + Module["_sqlite3_create_function_v2"] = wasmExports["sqlite3_create_function_v2"]; + Module["_sqlite3_create_window_function"] = wasmExports["sqlite3_create_window_function"]; + Module["_sqlite3_overload_function"] = wasmExports["sqlite3_overload_function"]; + Module["_sqlite3_trace_v2"] = wasmExports["sqlite3_trace_v2"]; + Module["_sqlite3_commit_hook"] = wasmExports["sqlite3_commit_hook"]; + Module["_sqlite3_update_hook"] = wasmExports["sqlite3_update_hook"]; + Module["_sqlite3_rollback_hook"] = wasmExports["sqlite3_rollback_hook"]; + Module["_sqlite3_preupdate_hook"] = wasmExports["sqlite3_preupdate_hook"]; + Module["_sqlite3_set_errmsg"] = wasmExports["sqlite3_set_errmsg"]; + Module["_sqlite3_error_offset"] = wasmExports["sqlite3_error_offset"]; + Module["_sqlite3_errcode"] = wasmExports["sqlite3_errcode"]; + Module["_sqlite3_extended_errcode"] = wasmExports["sqlite3_extended_errcode"]; + Module["_sqlite3_errstr"] = wasmExports["sqlite3_errstr"]; + Module["_sqlite3_limit"] = wasmExports["sqlite3_limit"]; + Module["_sqlite3_open"] = wasmExports["sqlite3_open"]; + Module["_sqlite3_open_v2"] = wasmExports["sqlite3_open_v2"]; + Module["_sqlite3_create_collation"] = wasmExports["sqlite3_create_collation"]; + Module["_sqlite3_create_collation_v2"] = wasmExports["sqlite3_create_collation_v2"]; + Module["_sqlite3_collation_needed"] = wasmExports["sqlite3_collation_needed"]; + Module["_sqlite3_get_autocommit"] = wasmExports["sqlite3_get_autocommit"]; + Module["_sqlite3_table_column_metadata"] = wasmExports["sqlite3_table_column_metadata"]; + Module["_sqlite3_extended_result_codes"] = wasmExports["sqlite3_extended_result_codes"]; + Module["_sqlite3_uri_key"] = wasmExports["sqlite3_uri_key"]; + Module["_sqlite3_uri_int64"] = wasmExports["sqlite3_uri_int64"]; + Module["_sqlite3_db_name"] = wasmExports["sqlite3_db_name"]; + Module["_sqlite3_db_filename"] = wasmExports["sqlite3_db_filename"]; + Module["_sqlite3_db_readonly"] = wasmExports["sqlite3_db_readonly"]; + Module["_sqlite3_compileoption_used"] = wasmExports["sqlite3_compileoption_used"]; + Module["_sqlite3_compileoption_get"] = wasmExports["sqlite3_compileoption_get"]; + Module["_sqlite3session_diff"] = wasmExports["sqlite3session_diff"]; + Module["_sqlite3session_attach"] = wasmExports["sqlite3session_attach"]; + Module["_sqlite3session_create"] = wasmExports["sqlite3session_create"]; + Module["_sqlite3session_delete"] = wasmExports["sqlite3session_delete"]; + Module["_sqlite3session_table_filter"] = wasmExports["sqlite3session_table_filter"]; + Module["_sqlite3session_changeset"] = wasmExports["sqlite3session_changeset"]; + Module["_sqlite3session_changeset_strm"] = wasmExports["sqlite3session_changeset_strm"]; + Module["_sqlite3session_patchset_strm"] = wasmExports["sqlite3session_patchset_strm"]; + Module["_sqlite3session_patchset"] = wasmExports["sqlite3session_patchset"]; + Module["_sqlite3session_enable"] = wasmExports["sqlite3session_enable"]; + Module["_sqlite3session_indirect"] = wasmExports["sqlite3session_indirect"]; + Module["_sqlite3session_isempty"] = wasmExports["sqlite3session_isempty"]; + Module["_sqlite3session_memory_used"] = wasmExports["sqlite3session_memory_used"]; + Module["_sqlite3session_object_config"] = wasmExports["sqlite3session_object_config"]; + Module["_sqlite3session_changeset_size"] = wasmExports["sqlite3session_changeset_size"]; + Module["_sqlite3changeset_start"] = wasmExports["sqlite3changeset_start"]; + Module["_sqlite3changeset_start_v2"] = wasmExports["sqlite3changeset_start_v2"]; + Module["_sqlite3changeset_start_strm"] = wasmExports["sqlite3changeset_start_strm"]; + Module["_sqlite3changeset_start_v2_strm"] = wasmExports["sqlite3changeset_start_v2_strm"]; + Module["_sqlite3changeset_next"] = wasmExports["sqlite3changeset_next"]; + Module["_sqlite3changeset_op"] = wasmExports["sqlite3changeset_op"]; + Module["_sqlite3changeset_pk"] = wasmExports["sqlite3changeset_pk"]; + Module["_sqlite3changeset_old"] = wasmExports["sqlite3changeset_old"]; + Module["_sqlite3changeset_new"] = wasmExports["sqlite3changeset_new"]; + Module["_sqlite3changeset_conflict"] = wasmExports["sqlite3changeset_conflict"]; + Module["_sqlite3changeset_fk_conflicts"] = wasmExports["sqlite3changeset_fk_conflicts"]; + Module["_sqlite3changeset_finalize"] = wasmExports["sqlite3changeset_finalize"]; + Module["_sqlite3changeset_invert"] = wasmExports["sqlite3changeset_invert"]; + Module["_sqlite3changeset_invert_strm"] = wasmExports["sqlite3changeset_invert_strm"]; + Module["_sqlite3changeset_apply_v2"] = wasmExports["sqlite3changeset_apply_v2"]; + Module["_sqlite3changeset_apply_v3"] = wasmExports["sqlite3changeset_apply_v3"]; + Module["_sqlite3changeset_apply"] = wasmExports["sqlite3changeset_apply"]; + Module["_sqlite3changeset_apply_v3_strm"] = wasmExports["sqlite3changeset_apply_v3_strm"]; + Module["_sqlite3changeset_apply_v2_strm"] = wasmExports["sqlite3changeset_apply_v2_strm"]; + Module["_sqlite3changeset_apply_strm"] = wasmExports["sqlite3changeset_apply_strm"]; + Module["_sqlite3changegroup_new"] = wasmExports["sqlite3changegroup_new"]; + Module["_sqlite3changegroup_add"] = wasmExports["sqlite3changegroup_add"]; + Module["_sqlite3changegroup_output"] = wasmExports["sqlite3changegroup_output"]; + Module["_sqlite3changegroup_add_strm"] = wasmExports["sqlite3changegroup_add_strm"]; + Module["_sqlite3changegroup_output_strm"] = wasmExports["sqlite3changegroup_output_strm"]; + Module["_sqlite3changegroup_delete"] = wasmExports["sqlite3changegroup_delete"]; + Module["_sqlite3changeset_concat"] = wasmExports["sqlite3changeset_concat"]; + Module["_sqlite3changeset_concat_strm"] = wasmExports["sqlite3changeset_concat_strm"]; + Module["_sqlite3session_config"] = wasmExports["sqlite3session_config"]; + Module["_sqlite3_sourceid"] = wasmExports["sqlite3_sourceid"]; + Module["_sqlite3__wasm_pstack_ptr"] = wasmExports["sqlite3__wasm_pstack_ptr"]; + Module["_sqlite3__wasm_pstack_restore"] = wasmExports["sqlite3__wasm_pstack_restore"]; + Module["_sqlite3__wasm_pstack_alloc"] = wasmExports["sqlite3__wasm_pstack_alloc"]; + Module["_sqlite3__wasm_pstack_remaining"] = wasmExports["sqlite3__wasm_pstack_remaining"]; + Module["_sqlite3__wasm_pstack_quota"] = wasmExports["sqlite3__wasm_pstack_quota"]; + Module["_sqlite3__wasm_test_struct"] = wasmExports["sqlite3__wasm_test_struct"]; + Module["_sqlite3__wasm_enum_json"] = wasmExports["sqlite3__wasm_enum_json"]; + Module["_sqlite3__wasm_vfs_unlink"] = wasmExports["sqlite3__wasm_vfs_unlink"]; + Module["_sqlite3__wasm_db_vfs"] = wasmExports["sqlite3__wasm_db_vfs"]; + Module["_sqlite3__wasm_db_reset"] = wasmExports["sqlite3__wasm_db_reset"]; + Module["_sqlite3__wasm_db_export_chunked"] = wasmExports["sqlite3__wasm_db_export_chunked"]; + Module["_sqlite3__wasm_db_serialize"] = wasmExports["sqlite3__wasm_db_serialize"]; + Module["_sqlite3__wasm_vfs_create_file"] = wasmExports["sqlite3__wasm_vfs_create_file"]; + Module["_sqlite3__wasm_posix_create_file"] = wasmExports["sqlite3__wasm_posix_create_file"]; + Module["_sqlite3__wasm_kvvfsMakeKey"] = wasmExports["sqlite3__wasm_kvvfsMakeKey"]; + Module["_sqlite3__wasm_kvvfs_methods"] = wasmExports["sqlite3__wasm_kvvfs_methods"]; + Module["_sqlite3__wasm_vtab_config"] = wasmExports["sqlite3__wasm_vtab_config"]; + Module["_sqlite3__wasm_db_config_ip"] = wasmExports["sqlite3__wasm_db_config_ip"]; + Module["_sqlite3__wasm_db_config_pii"] = wasmExports["sqlite3__wasm_db_config_pii"]; + Module["_sqlite3__wasm_db_config_s"] = wasmExports["sqlite3__wasm_db_config_s"]; + Module["_sqlite3__wasm_config_i"] = wasmExports["sqlite3__wasm_config_i"]; + Module["_sqlite3__wasm_config_ii"] = wasmExports["sqlite3__wasm_config_ii"]; + Module["_sqlite3__wasm_config_j"] = wasmExports["sqlite3__wasm_config_j"]; + Module["_sqlite3__wasm_qfmt_token"] = wasmExports["sqlite3__wasm_qfmt_token"]; + Module["_sqlite3__wasm_kvvfs_decode"] = wasmExports["sqlite3__wasm_kvvfs_decode"]; + Module["_sqlite3__wasm_kvvfs_encode"] = wasmExports["sqlite3__wasm_kvvfs_encode"]; + Module["_sqlite3__wasm_init_wasmfs"] = wasmExports["sqlite3__wasm_init_wasmfs"]; + Module["_sqlite3__wasm_test_intptr"] = wasmExports["sqlite3__wasm_test_intptr"]; + Module["_sqlite3__wasm_test_voidptr"] = wasmExports["sqlite3__wasm_test_voidptr"]; + Module["_sqlite3__wasm_test_int64_max"] = wasmExports["sqlite3__wasm_test_int64_max"]; + Module["_sqlite3__wasm_test_int64_min"] = wasmExports["sqlite3__wasm_test_int64_min"]; + Module["_sqlite3__wasm_test_int64_times2"] = wasmExports["sqlite3__wasm_test_int64_times2"]; + Module["_sqlite3__wasm_test_int64_minmax"] = wasmExports["sqlite3__wasm_test_int64_minmax"]; + Module["_sqlite3__wasm_test_int64ptr"] = wasmExports["sqlite3__wasm_test_int64ptr"]; + Module["_sqlite3__wasm_test_stack_overflow"] = wasmExports["sqlite3__wasm_test_stack_overflow"]; + Module["_sqlite3__wasm_test_str_hello"] = wasmExports["sqlite3__wasm_test_str_hello"]; + Module["_sqlite3__wasm_SQLTester_strglob"] = wasmExports["sqlite3__wasm_SQLTester_strglob"]; + Module["_malloc"] = wasmExports["malloc"]; + Module["_free"] = wasmExports["free"]; + Module["_realloc"] = wasmExports["realloc"]; + _emscripten_builtin_memalign = wasmExports["emscripten_builtin_memalign"]; + wasmExports["_emscripten_stack_restore"]; + wasmExports["_emscripten_stack_alloc"]; + wasmExports["emscripten_stack_get_current"]; + wasmExports["__indirect_function_table"]; + } + var wasmImports = { + __syscall_chmod: ___syscall_chmod, + __syscall_faccessat: ___syscall_faccessat, + __syscall_fchmod: ___syscall_fchmod, + __syscall_fchown32: ___syscall_fchown32, + __syscall_fcntl64: ___syscall_fcntl64, + __syscall_fstat64: ___syscall_fstat64, + __syscall_ftruncate64: ___syscall_ftruncate64, + __syscall_getcwd: ___syscall_getcwd, + __syscall_ioctl: ___syscall_ioctl, + __syscall_lstat64: ___syscall_lstat64, + __syscall_mkdirat: ___syscall_mkdirat, + __syscall_newfstatat: ___syscall_newfstatat, + __syscall_openat: ___syscall_openat, + __syscall_readlinkat: ___syscall_readlinkat, + __syscall_rmdir: ___syscall_rmdir, + __syscall_stat64: ___syscall_stat64, + __syscall_unlinkat: ___syscall_unlinkat, + __syscall_utimensat: ___syscall_utimensat, + _localtime_js: __localtime_js, + _mmap_js: __mmap_js, + _munmap_js: __munmap_js, + _tzset_js: __tzset_js, + clock_time_get: _clock_time_get, + emscripten_date_now: _emscripten_date_now, + emscripten_get_heap_max: _emscripten_get_heap_max, + emscripten_get_now: _emscripten_get_now, + emscripten_resize_heap: _emscripten_resize_heap, + environ_get: _environ_get, + environ_sizes_get: _environ_sizes_get, + fd_close: _fd_close, + fd_fdstat_get: _fd_fdstat_get, + fd_read: _fd_read, + fd_seek: _fd_seek, + fd_sync: _fd_sync, + fd_write: _fd_write, + memory: wasmMemory + }; + function run() { + if (runDependencies > 0) { + dependenciesFulfilled = run; + return; + } + preRun(); + if (runDependencies > 0) { + dependenciesFulfilled = run; + return; + } + function doRun() { + Module["calledRun"] = true; + if (ABORT) return; + initRuntime(); + readyPromiseResolve?.(Module); + Module["onRuntimeInitialized"]?.(); + postRun(); + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(() => { + setTimeout(() => Module["setStatus"](""), 1); + doRun(); + }, 1); + } else doRun(); + } + var wasmExports = await createWasm(); + run(); + /** + post-js-header.js is to be prepended to other code to create + post-js.js for use with Emscripten's --post-js flag, so it gets + injected in the earliest stages of sqlite3InitModule(). + + Running this function will bootstrap the library and return + a Promise to the sqlite3 namespace object. + + In the canonical builds, this gets called by extern-post-js.c-pp.js + */ + Module.runSQLite3PostLoadInit = async function(sqlite3InitScriptInfo, EmscriptenModule, sqlite3IsUnderTest) { + /** ^^^ Don't use Module.postRun, as that runs a different time + depending on whether this file is built with emcc 3.1.x or + 4.0.x. This function name is intentionally obnoxiously verbose to + ensure that we don't collide with current and future Emscripten + symbol names. */ + "use strict"; + delete EmscriptenModule.runSQLite3PostLoadInit; + globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(apiConfig = globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) { + if (sqlite3ApiBootstrap.sqlite3) { + (sqlite3ApiBootstrap.sqlite3.config || console).warn("sqlite3ApiBootstrap() called multiple times.", "Config and external initializers are ignored on calls after the first."); + return sqlite3ApiBootstrap.sqlite3; + } + const config = Object.assign(Object.create(null), { + exports: void 0, + memory: void 0, + bigIntEnabled: !!globalThis.BigInt64Array, + debug: console.debug.bind(console), + warn: console.warn.bind(console), + error: console.error.bind(console), + log: console.log.bind(console), + wasmfsOpfsDir: "/opfs", + useStdAlloc: false + }, apiConfig || {}); + Object.assign(config, { + allocExportName: config.useStdAlloc ? "malloc" : "sqlite3_malloc", + deallocExportName: config.useStdAlloc ? "free" : "sqlite3_free", + reallocExportName: config.useStdAlloc ? "realloc" : "sqlite3_realloc" + }); + [ + "exports", + "memory", + "functionTable", + "wasmfsOpfsDir" + ].forEach((k) => { + if ("function" === typeof config[k]) config[k] = config[k](); + }); + /** + The main sqlite3 binding API gets installed into this object, + mimicking the C API as closely as we can. The numerous members + names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as + possible, identically to the C-native counterparts, as documented at: + + https://sqlite.org/c3ref/intro.html + + A very few exceptions require an additional level of proxy + function or may otherwise require special attention in the WASM + environment, and all such cases are documented somewhere below + in this file or in sqlite3-api-glue.js. capi members which are + not documented are installed as 1-to-1 proxies for their + C-side counterparts. + */ + const capi = Object.create(null); + /** + Holds state which are specific to the WASM-related + infrastructure and glue code. + + Note that a number of members of this object are injected + dynamically after the api object is fully constructed, so + not all are documented in this file. + */ + const wasm = Object.create(null); + /** Internal helper for SQLite3Error ctor. */ + const __rcStr = (rc) => { + return capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc) || "Unknown result code #" + rc; + }; + /** Internal helper for SQLite3Error ctor. */ + const isInt32 = (n) => "number" === typeof n && n === (n | 0) && n <= 2147483647 && n >= -2147483648; + /** + An Error subclass specifically for reporting DB-level errors and + enabling clients to unambiguously identify such exceptions. + The C-level APIs never throw, but some of the higher-level + C-style APIs do and the object-oriented APIs use exceptions + exclusively to report errors. + */ + class SQLite3Error extends Error { + /** + Constructs this object with a message depending on its arguments: + + If its first argument is an integer, it is assumed to be + an SQLITE_... result code and it is passed to + sqlite3.capi.sqlite3_js_rc_str() to stringify it. + + If called with exactly 2 arguments and the 2nd is an object, + that object is treated as the 2nd argument to the parent + constructor. + + The exception's message is created by concatenating its + arguments with a space between each, except for the + two-args-with-an-object form and that the first argument will + get coerced to a string, as described above, if it's an + integer. + + If passed an integer first argument, the error object's + `resultCode` member will be set to the given integer value, + else it will be set to capi.SQLITE_ERROR. + */ + constructor(...args) { + let rc; + if (args.length) if (isInt32(args[0])) { + rc = args[0]; + if (1 === args.length) super(__rcStr(args[0])); + else { + const rcStr = __rcStr(rc); + if ("object" === typeof args[1]) super(rcStr, args[1]); + else { + args[0] = rcStr + ":"; + super(args.join(" ")); + } + } + } else if (2 === args.length && "object" === typeof args[1]) super(...args); + else super(args.join(" ")); + this.resultCode = rc || capi.SQLITE_ERROR; + this.name = "SQLite3Error"; + } + } + /** + Functionally equivalent to the SQLite3Error constructor but may + be used as part of an expression, e.g.: + + ``` + return someFunction(x) || SQLite3Error.toss(...); + ``` + */ + SQLite3Error.toss = (...args) => { + throw new SQLite3Error(...args); + }; + const toss3 = SQLite3Error.toss; + if (config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)) toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'."); + /** + Returns true if the given BigInt value is small enough to fit + into an int64 value, else false. + */ + const bigIntFits64 = function f(b) { + if (!f._max) { + f._max = BigInt("0x7fffffffffffffff"); + f._min = ~f._max; + } + return b >= f._min && b <= f._max; + }; + /** + Returns true if the given BigInt value is small enough to fit + into an int32, else false. + */ + const bigIntFits32 = (b) => b >= -2147483647n - 1n && b <= 2147483647n; + /** + Returns true if the given BigInt value is small enough to fit + into a double value without loss of precision, else false. + */ + const bigIntFitsDouble = function f(b) { + if (!f._min) { + f._min = Number.MIN_SAFE_INTEGER; + f._max = Number.MAX_SAFE_INTEGER; + } + return b >= f._min && b <= f._max; + }; + /** Returns v if v appears to be a TypedArray, else false. */ + const isTypedArray = (v) => { + return v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT) ? v : false; + }; + /** + Returns true if v appears to be one of our bind()-able TypedArray + types: Uint8Array or Int8Array or ArrayBuffer. Support for + TypedArrays with element sizes >1 is a potential TODO just + waiting on a use case to justify them. Until then, their `buffer` + property can be used to pass them as an ArrayBuffer. If it's not + a bindable array type, a falsy value is returned. + */ + const isBindableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); + /** + Returns true if v appears to be one of the TypedArray types + which is legal for holding SQL code (as opposed to binary blobs). + + Currently this is the same as isBindableTypedArray() but it + seems likely that we'll eventually want to add Uint32Array + and friends to the isBindableTypedArray() list but not to the + isSQLableTypedArray() list. + */ + const isSQLableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); + /** Returns true if isBindableTypedArray(v) does, else throws with a message + that v is not a supported TypedArray value. */ + const affirmBindableTypedArray = (v) => isBindableTypedArray(v) || toss3("Value is not of a supported TypedArray type."); + /** + If v is-a Array, its join("") result is returned. If + isSQLableTypedArray(v) is true then wasm.typedArrayToString(v) is + returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is + returned. Else v is returned as-is. + + Reminder to self: the "return as-is" instead of returning ''+v is + arguably a design mistake but changing it is risky at this point. + */ + const flexibleString = function(v) { + if (isSQLableTypedArray(v)) return wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v, 0, v.length); + else if (Array.isArray(v)) return v.join(""); + else if (wasm.isPtr(v)) v = wasm.cstrToJs(v); + return v; + }; + /** + An Error subclass specifically for reporting Wasm-level malloc() + failure and enabling clients to unambiguously identify such + exceptions. + */ + class WasmAllocError extends Error { + /** + If called with 2 arguments and the 2nd one is an object, it + behaves like the Error constructor, else it concatenates all + arguments together with a single space between each to + construct an error message string. As a special case, if + called with no arguments then it uses a default error + message. + */ + constructor(...args) { + if (2 === args.length && "object" === typeof args[1]) super(...args); + else if (args.length) super(args.join(" ")); + else super("Allocation failed."); + this.resultCode = capi.SQLITE_NOMEM; + this.name = "WasmAllocError"; + } + } + /** + Functionally equivalent to the WasmAllocError constructor but may + be used as part of an expression, e.g.: + + ``` + return someAllocatingFunction(x) || WasmAllocError.toss(...); + ``` + */ + WasmAllocError.toss = (...args) => { + throw new WasmAllocError(...args); + }; + Object.assign(capi, { + sqlite3_bind_blob: void 0, + sqlite3_bind_text: void 0, + sqlite3_create_function_v2: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) => {}, + sqlite3_create_function: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) => {}, + sqlite3_create_window_function: (pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) => {}, + sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags, stmtPtrPtr, strPtrPtr) => {}, + sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen, stmtPtrPtr, strPtrPtr) => {}, + sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg) => {}, + sqlite3_randomness: (n, outPtr) => {} + }); + /** + Various internal-use utilities are added here as needed. They + are bound to an object only so that we have access to them in + the differently-scoped steps of the API bootstrapping + process. At the end of the API setup process, this object gets + removed. These are NOT part of the public API. + */ + const util = { + affirmBindableTypedArray, + flexibleString, + bigIntFits32, + bigIntFits64, + bigIntFitsDouble, + isBindableTypedArray, + isInt32, + isSQLableTypedArray, + isTypedArray, + isUIThread: () => globalThis.window === globalThis && !!globalThis.document, + toss: function(...args) { + throw new Error(args.join(" ")); + }, + toss3, + typedArrayPart: wasm.typedArrayPart, + assert: function(arg, msg) { + if (!arg) util.toss("Assertion failed:", msg); + }, + affirmDbHeader: function(bytes) { + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + const header = "SQLite format 3"; + if (15 > bytes.byteLength) toss3("Input does not contain an SQLite3 database header."); + for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss3("Input does not contain an SQLite3 database header."); + }, + affirmIsDb: function(bytes) { + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + const n = bytes.byteLength; + if (n < 512 || n % 512 !== 0) toss3("Byte array size", n, "is invalid for an SQLite3 db."); + util.affirmDbHeader(bytes); + } + }; + /** + wasm.X properties which are used for configuring the wasm + environment via whwashutil.js. This object gets fleshed out with + a number of WASM-specific utilities, in sqlite3-api-glue.c-pp.js. + */ + Object.assign(wasm, { + exports: config.exports || toss3("Missing API config.exports (WASM module exports)."), + memory: config.memory || config.exports["memory"] || toss3("API config object requires a WebAssembly.Memory object", "in either config.exports.memory (exported)", "or config.memory (imported)."), + pointerSize: "number" === typeof config.exports.sqlite3_libversion() ? 4 : 8, + bigIntEnabled: !!config.bigIntEnabled, + functionTable: config.functionTable, + alloc: void 0, + realloc: void 0, + dealloc: void 0 + }); + /** + wasm.alloc()'s srcTypedArray.byteLength bytes, + populates them with the values from the source + TypedArray, and returns the pointer to that memory. The + returned pointer must eventually be passed to + wasm.dealloc() to clean it up. + + The argument may be a Uint8Array, Int8Array, or ArrayBuffer, + and it throws if passed any other type. + + As a special case, to avoid further special cases where + this is used, if srcTypedArray.byteLength is 0, it + allocates a single byte and sets it to the value + 0. Even in such cases, calls must behave as if the + allocated memory has exactly srcTypedArray.byteLength + bytes. + */ + wasm.allocFromTypedArray = function(srcTypedArray) { + if (srcTypedArray instanceof ArrayBuffer) srcTypedArray = new Uint8Array(srcTypedArray); + affirmBindableTypedArray(srcTypedArray); + const pRet = wasm.alloc(srcTypedArray.byteLength || 1); + wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], Number(pRet)); + return pRet; + }; + { + const keyAlloc = config.allocExportName, keyDealloc = config.deallocExportName, keyRealloc = config.reallocExportName; + for (const key of [ + keyAlloc, + keyDealloc, + keyRealloc + ]) if (!(wasm.exports[key] instanceof Function)) toss3("Missing required exports[", key, "] function."); + wasm.alloc = function f(n) { + return f.impl(n) || WasmAllocError.toss("Failed to allocate", n, " bytes."); + }; + wasm.alloc.impl = wasm.exports[keyAlloc]; + wasm.realloc = function f(m, n) { + const m2 = f.impl(wasm.ptr.coerce(m), n); + return n ? m2 || WasmAllocError.toss("Failed to reallocate", n, " bytes.") : wasm.ptr.null; + }; + wasm.realloc.impl = wasm.exports[keyRealloc]; + wasm.dealloc = function f(m) { + f.impl(wasm.ptr.coerce(m)); + }; + wasm.dealloc.impl = wasm.exports[keyDealloc]; + } + /** + Reports info about compile-time options using + sqlite3_compileoption_get() and sqlite3_compileoption_used(). It + has several distinct uses: + + If optName is an array then it is expected to be a list of + compilation options and this function returns an object + which maps each such option to true or false, indicating + whether or not the given option was included in this + build. That object is returned. + + If optName is an object, its keys are expected to be compilation + options and this function sets each entry to true or false, + indicating whether the compilation option was used or not. That + object is returned. + + If passed no arguments then it returns an object mapping + all known compilation options to their compile-time values, + or boolean true if they are defined with no value. This + result, which is relatively expensive to compute, is cached + and returned for future no-argument calls. + + In all other cases it returns true if the given option was + active when when compiling the sqlite3 module, else false. + + Compile-time option names may optionally include their + "SQLITE_" prefix. When it returns an object of all options, + the prefix is elided. + */ + wasm.compileOptionUsed = function f(optName) { + if (!arguments.length) { + if (f._result) return f._result; + else if (!f._opt) { + f._rx = /^([^=]+)=(.+)/; + f._rxInt = /^-?\d+$/; + f._opt = function(opt, rv) { + const m = f._rx.exec(opt); + rv[0] = m ? m[1] : opt; + rv[1] = m ? f._rxInt.test(m[2]) ? +m[2] : m[2] : true; + }; + } + const rc = Object.create(null), ov = [0, 0]; + let i = 0, k; + while (k = capi.sqlite3_compileoption_get(i++)) { + f._opt(k, ov); + rc[ov[0]] = ov[1]; + } + return f._result = rc; + } else if (Array.isArray(optName)) { + const rc = Object.create(null); + optName.forEach((v) => { + rc[v] = capi.sqlite3_compileoption_used(v); + }); + return rc; + } else if ("object" === typeof optName) { + Object.keys(optName).forEach((k) => { + optName[k] = capi.sqlite3_compileoption_used(k); + }); + return optName; + } + return "string" === typeof optName ? !!capi.sqlite3_compileoption_used(optName) : false; + }; + /** + sqlite3.wasm.pstack (pseudo-stack) holds a special-case allocator + intended solely for short-lived, small data. In practice, it's + primarily used to allocate output pointers. It must not be used + for any memory which needs to outlive the scope in which it's + obtained from pstack. + + The library guarantees only that a minimum of 2kb are available + in this allocator, and it may provide more (it's a build-time + value). pstack.quota and pstack.remaining can be used to get the + total resp. remaining amount of memory. + + It has only a single intended usage pattern: + + ``` + const stackPos = pstack.pointer; + try{ + const ptr = pstack.alloc(8); + // ==> pstack.pointer === ptr + const otherPtr = pstack.alloc(8); + // ==> pstack.pointer === otherPtr + ... + }finally{ + pstack.restore(stackPos); + // ==> pstack.pointer === stackPos + } + ``` + + This allocator is much faster than a general-purpose one but is + limited to usage patterns like the one shown above (which are + pretty common when using sqlite3.capi). + + The memory lives in the WASM heap and can be used with routines + such as wasm.poke() and wasm.heap8u().slice(). + */ + wasm.pstack = Object.assign(Object.create(null), { + restore: wasm.exports.sqlite3__wasm_pstack_restore, + alloc: function(n) { + if ("string" === typeof n && !(n = wasm.sizeofIR(n))) WasmAllocError.toss("Invalid value for pstack.alloc(", arguments[0], ")"); + return wasm.exports.sqlite3__wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate", n, "bytes from the pstack."); + }, + allocChunks: function(n, sz) { + if ("string" === typeof sz && !(sz = wasm.sizeofIR(sz))) WasmAllocError.toss("Invalid size value for allocChunks(", arguments[1], ")"); + const mem = wasm.pstack.alloc(n * sz); + const rc = [mem]; + let i = 1, offset = sz; + for (; i < n; ++i, offset += sz) rc.push(wasm.ptr.add(mem, offset)); + return rc; + }, + allocPtr: (n = 1, safePtrSize = true) => { + return 1 === n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptr.size) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptr.size); + }, + call: function(f) { + const stackPos = wasm.pstack.pointer; + try { + return f(sqlite3); + } finally { + wasm.pstack.restore(stackPos); + } + } + }); + Object.defineProperties(wasm.pstack, { + pointer: { + configurable: false, + iterable: true, + writeable: false, + get: wasm.exports.sqlite3__wasm_pstack_ptr + }, + quota: { + configurable: false, + iterable: true, + writeable: false, + get: wasm.exports.sqlite3__wasm_pstack_quota + }, + remaining: { + configurable: false, + iterable: true, + writeable: false, + get: wasm.exports.sqlite3__wasm_pstack_remaining + } + }); + /** + Docs: https://sqlite.org/wasm/doc/trunk/api-c-style.md#sqlite3_randomness + */ + capi.sqlite3_randomness = (...args) => { + if (1 === args.length && util.isTypedArray(args[0]) && 1 === args[0].BYTES_PER_ELEMENT) { + const ta = args[0]; + if (0 === ta.byteLength) { + wasm.exports.sqlite3_randomness(0, wasm.ptr.null); + return ta; + } + const stack = wasm.pstack.pointer; + try { + let n = ta.byteLength, offset = 0; + const r = wasm.exports.sqlite3_randomness; + const heap = wasm.heap8u(); + const nAlloc = n < 512 ? n : 512; + const ptr = wasm.pstack.alloc(nAlloc); + do { + const j = n > nAlloc ? nAlloc : n; + r(j, ptr); + ta.set(wasm.typedArrayPart(heap, ptr, wasm.ptr.add(ptr, j)), offset); + n -= j; + offset += j; + } while (n > 0); + } catch (e) { + config.error("Highly unexpected (and ignored!) exception in sqlite3_randomness():", e); + } finally { + wasm.pstack.restore(stack); + } + return ta; + } + wasm.exports.sqlite3_randomness(...args); + }; + /** + If the wasm environment has a WASMFS/OPFS-backed persistent + storage directory, its path is returned by this function. If it + does not then it returns "" (noting that "" is a falsy value). + + The first time this is called, this function inspects the current + environment to determine whether WASMFS persistence support is + available and, if it is, enables it (if needed). After the first + call it always returns the cached result. + + If the returned string is not empty, any files stored under the + returned path (recursively) are housed in OPFS storage. If the + returned string is empty, this particular persistent storage + option is not available on the client. + + Though the mount point name returned by this function is intended + to remain stable, clients should not hard-coded it anywhere. + Always call this function to get the path. + + This function is a no-op in most builds of this library, as the + WASMFS capability requires a custom build. + */ + capi.sqlite3_wasmfs_opfs_dir = function() { + if (void 0 !== this.dir) return this.dir; + const pdir = config.wasmfsOpfsDir; + if (!pdir || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !wasm.exports.sqlite3__wasm_init_wasmfs) return this.dir = ""; + try { + if (pdir && 0 === wasm.xCallWrapped("sqlite3__wasm_init_wasmfs", "i32", ["string"], pdir)) return this.dir = pdir; + else return this.dir = ""; + } catch (e) { + return this.dir = ""; + } + }.bind(Object.create(null)); + /** + Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a + non-empty string and the given name starts with (that string + + '/'), else returns false. + */ + capi.sqlite3_wasmfs_filename_is_persistent = function(name) { + const p = capi.sqlite3_wasmfs_opfs_dir(); + return p && name ? name.startsWith(p + "/") : false; + }; + /** + Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name + (defaulting to "main"), returns a truthy value (see below) if + that db uses that VFS, else returns false. If pDb is falsy then + the 3rd argument is ignored and this function returns a truthy + value if the default VFS name matches that of the 2nd argument. + Results are undefined if pDb is truthy but refers to an invalid + pointer. The 3rd argument specifies the database name of the + given database connection to check, defaulting to the main db. + + The 2nd and 3rd arguments may either be a JS string or a WASM + C-string. If the 2nd argument is a NULL WASM pointer, the default + VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is + assumed. + + The truthy value it returns is a pointer to the `sqlite3_vfs` + object. + + To permit safe use of this function from APIs which may be called + via C (like SQL UDFs), this function does not throw: if bad + arguments cause a conversion error when passing into wasm-space, + false is returned. + */ + capi.sqlite3_js_db_uses_vfs = function(pDb, vfsName, dbName = 0) { + try { + const pK = capi.sqlite3_vfs_find(vfsName); + if (!pK) return false; + else if (!pDb) return pK === capi.sqlite3_vfs_find(0) ? pK : false; + else return pK === capi.sqlite3_js_db_vfs(pDb, dbName) ? pK : false; + } catch (e) { + return false; + } + }; + /** + Returns an array of the names of all currently-registered sqlite3 + VFSes. + */ + capi.sqlite3_js_vfs_list = function() { + const rc = []; + let pVfs = capi.sqlite3_vfs_find(wasm.ptr.null); + while (pVfs) { + const oVfs = new capi.sqlite3_vfs(pVfs); + rc.push(wasm.cstrToJs(oVfs.$zName)); + pVfs = oVfs.$pNext; + oVfs.dispose(); + } + return rc; + }; + /** + A convenience wrapper around sqlite3_serialize() which serializes + the given `sqlite3*` pointer to a Uint8Array. The first argument + may be either an `sqlite3*` or an sqlite3.oo1.DB instance. + + On success it returns a Uint8Array. If the schema is empty, an + empty array is returned. + + `schema` is the schema to serialize. It may be a WASM C-string + pointer or a JS string. If it is falsy, it defaults to `"main"`. + + On error it throws with a description of the problem. + */ + capi.sqlite3_js_db_export = function(pDb, schema = 0) { + pDb = wasm.xWrap.testConvertArg("sqlite3*", pDb); + if (!pDb) toss3("Invalid sqlite3* argument."); + if (!wasm.bigIntEnabled) toss3("BigInt support is not enabled."); + const scope = wasm.scopedAllocPush(); + let pOut; + try { + const pSize = wasm.scopedAlloc(8 + wasm.ptr.size); + const ppOut = wasm.ptr.add(pSize, 8); + /** + Maintenance reminder, since this cost a full hour of grief + and confusion: if the order of pSize/ppOut are reversed in + that memory block, fetching the value of pSize after the + export reads a garbage size because it's not on an 8-byte + memory boundary! + */ + const zSchema = schema ? wasm.isPtr(schema) ? schema : wasm.scopedAllocCString("" + schema) : wasm.ptr.null; + let rc = wasm.exports.sqlite3__wasm_db_serialize(pDb, zSchema, ppOut, pSize, 0); + if (rc) toss3("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc)); + pOut = wasm.peekPtr(ppOut); + const nOut = wasm.peek(pSize, "i64"); + rc = nOut ? wasm.heap8u().slice(Number(pOut), Number(pOut) + Number(nOut)) : new Uint8Array(); + return rc; + } finally { + if (pOut) wasm.exports.sqlite3_free(pOut); + wasm.scopedAllocPop(scope); + } + }; + /** + Given a `sqlite3*` and a database name (JS string or WASM + C-string pointer, which may be 0), returns a pointer to the + sqlite3_vfs responsible for it. If the given db name is null/0, + or not provided, then "main" is assumed. + */ + capi.sqlite3_js_db_vfs = (dbPointer, dbName = wasm.ptr.null) => util.sqlite3__wasm_db_vfs(dbPointer, dbName); + /** + A thin wrapper around capi.sqlite3_aggregate_context() which + behaves the same except that it throws a WasmAllocError if that + function returns 0. As a special case, if n is falsy it does + _not_ throw if that function returns 0. That special case is + intended for use with xFinal() implementations. + */ + capi.sqlite3_js_aggregate_context = (pCtx, n) => { + return capi.sqlite3_aggregate_context(pCtx, n) || (n ? WasmAllocError.toss("Cannot allocate", n, "bytes for sqlite3_aggregate_context()") : 0); + }; + /** + If the current environment supports the POSIX file APIs, this routine + creates (or overwrites) the given file using those APIs. This is + primarily intended for use in Emscripten-based builds where the POSIX + APIs are transparently proxied by an in-memory virtual filesystem. + It may behave differently in other environments. + + The first argument must be either a JS string or WASM C-string + holding the filename. This routine does _not_ create intermediary + directories if the filename has a directory part. + + The 2nd argument may either a valid WASM memory pointer, an + ArrayBuffer, or a Uint8Array. The 3rd must be the length, in + bytes, of the data array to copy. If the 2nd argument is an + ArrayBuffer or Uint8Array and the 3rd is not a positive integer + then the 3rd defaults to the array's byteLength value. + + Results are undefined if data is a WASM pointer and dataLen is + exceeds data's bounds. + + Throws if any arguments are invalid or if creating or writing to + the file fails. + + Added in 3.43 as an alternative for the deprecated + sqlite3_js_vfs_create_file(). + */ + capi.sqlite3_js_posix_create_file = function(filename, data, dataLen) { + let pData; + if (data && wasm.isPtr(data)) pData = data; + else if (data instanceof ArrayBuffer || data instanceof Uint8Array) { + pData = wasm.allocFromTypedArray(data); + if (arguments.length < 3 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; + } else SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file()."); + try { + if (!util.isInt32(dataLen) || dataLen < 0) SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file()."); + const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen); + if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); + } finally { + if (pData && pData !== data) wasm.dealloc(pData); + } + }; + /** + Deprecation warning: this function does not work properly in + debug builds of sqlite3 because its out-of-scope use of the + sqlite3_vfs API triggers assertions in the core library. That + was unfortunately not discovered until 2023-08-11. This function + is now deprecated. It should not be used in new code and should + be removed from existing code. + + Alternative options: + + - The "unix" VFS and its variants can get equivalent + functionality with sqlite3_js_posix_create_file(). + + - OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs" + VFS, or the importDb() method of the PoolUtil object provided + by the "opfs-sahpool" OPFS (noting that its VFS name may differ + depending on client-side configuration). We cannot proxy those + from here because the former is necessarily asynchronous and + the latter requires information not available to this function. + + Historical (deprecated) behaviour: + + Creates a file using the storage appropriate for the given + sqlite3_vfs. The first argument may be a VFS name (JS string + only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or + a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the + default VFS. If passed a string which does not resolve using + sqlite3_vfs_find(), an exception is thrown. (Note that a WASM + C-string is not accepted because it is impossible to + distinguish from a C-level `sqlite3_vfs*`.) + + The second argument, the filename, must be a JS or WASM C-string. + + The 3rd may either be falsy, a valid WASM memory pointer, an + ArrayBuffer, or a Uint8Array. The 4th must be the length, in + bytes, of the data array to copy. If the 3rd argument is an + ArrayBuffer or Uint8Array and the 4th is not a positive integer + then the 4th defaults to the array's byteLength value. + + If data is falsy then a file is created with dataLen bytes filled + with uninitialized data (whatever truncate() leaves there). If + data is not falsy then a file is created or truncated and it is + filled with the first dataLen bytes of the data source. + + Throws if any arguments are invalid or if creating or writing to + the file fails. + + Note that most VFSes do _not_ automatically create directory + parts of filenames, nor do all VFSes have a concept of + directories. If the given filename is not valid for the given + VFS, an exception will be thrown. This function exists primarily + to assist in implementing file-upload capability, with the caveat + that clients must have some idea of the VFS into which they want + to upload and that VFS must support the operation. + + VFS-specific notes: + + - "memdb": results are undefined. + + - "kvvfs": will fail with an I/O error due to strict internal + requirements of that VFS's xTruncate(). + + - "unix" and related: will use the WASM build's equivalent of the + POSIX I/O APIs. This will work so long as neither a specific + VFS nor the WASM environment imposes requirements which break + it. (Much later: it turns out that debug builds of the library + impose such requirements, in that they assert() that dataLen is + an even multiple of a valid db page size.) + + - "opfs": uses OPFS storage and creates directory parts of the + filename. It can only be used to import an SQLite3 database + file and will fail if given anything else. + */ + capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen) { + config.warn("sqlite3_js_vfs_create_file() is deprecated and", "should be avoided because it can lead to C-level crashes.", "See its documentation for alternatives."); + let pData; + if (data) if (wasm.isPtr(data)) pData = data; + else { + if (data instanceof ArrayBuffer) data = new Uint8Array(data); + if (data instanceof Uint8Array) { + pData = wasm.allocFromTypedArray(data); + if (arguments.length < 4 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; + } else SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file()."); + } + else pData = 0; + if (!util.isInt32(dataLen) || dataLen < 0) { + if (pData && pData !== data) wasm.dealloc(pData); + SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file()."); + } + try { + const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen); + if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); + } finally { + if (pData && pData !== data) wasm.dealloc(pData); + } + }; + /** + Converts SQL input from a variety of convenient formats + to plain strings. + + If v is a string, it is returned as-is. If it is-a Array, its + join("") result is returned. If is is a Uint8Array, Int8Array, + or ArrayBuffer, it is assumed to hold UTF-8-encoded text and is + decoded to a string. If it looks like a WASM pointer, + wasm.cstrToJs(sql) is returned. Else undefined is returned. + + Added in 3.44 + */ + capi.sqlite3_js_sql_to_string = (sql) => { + if ("string" === typeof sql) return sql; + const x = flexibleString(v); + return x === v ? void 0 : x; + }; + /** + Wraps all known variants of the C-side variadic + sqlite3_db_config(). + + Full docs: https://sqlite.org/c3ref/db_config.html + + Returns capi.SQLITE_MISUSE if op is not a valid operation ID. + + The variants which take `(int, int*)` arguments treat a + missing or falsy pointer argument as 0. + */ + capi.sqlite3_db_config = function(pDb, op, ...args) { + switch (op) { + case capi.SQLITE_DBCONFIG_ENABLE_FKEY: + case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: + case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: + case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: + case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: + case capi.SQLITE_DBCONFIG_ENABLE_QPSG: + case capi.SQLITE_DBCONFIG_TRIGGER_EQP: + case capi.SQLITE_DBCONFIG_RESET_DATABASE: + case capi.SQLITE_DBCONFIG_DEFENSIVE: + case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA: + case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: + case capi.SQLITE_DBCONFIG_DQS_DML: + case capi.SQLITE_DBCONFIG_DQS_DDL: + case capi.SQLITE_DBCONFIG_ENABLE_VIEW: + case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: + case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA: + case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS: + case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER: + case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: + case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: + case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: + if (!this.ip) this.ip = wasm.xWrap("sqlite3__wasm_db_config_ip", "int", [ + "sqlite3*", + "int", + "int", + "*" + ]); + return this.ip(pDb, op, args[0], args[1] || 0); + case capi.SQLITE_DBCONFIG_LOOKASIDE: + if (!this.pii) this.pii = wasm.xWrap("sqlite3__wasm_db_config_pii", "int", [ + "sqlite3*", + "int", + "*", + "int", + "int" + ]); + return this.pii(pDb, op, args[0], args[1], args[2]); + case capi.SQLITE_DBCONFIG_MAINDBNAME: + if (!this.s) this.s = wasm.xWrap("sqlite3__wasm_db_config_s", "int", [ + "sqlite3*", + "int", + "string:static" + ]); + return this.s(pDb, op, args[0]); + default: return capi.SQLITE_MISUSE; + } + }.bind(Object.create(null)); + /** + Given a (sqlite3_value*), this function attempts to convert it + to an equivalent JS value with as much fidelity as feasible and + return it. + + By default it throws if it cannot determine any sensible + conversion. If passed a falsy second argument, it instead returns + `undefined` if no suitable conversion is found. Note that there + is no conversion from SQL to JS which results in the `undefined` + value, so `undefined` has an unambiguous meaning here. It will + always throw a WasmAllocError if allocating memory for a + conversion fails. + + Caveats: + + - It does not support sqlite3_value_to_pointer() conversions + because those require a type name string which this function + does not have and cannot sensibly be given at the level of the + API where this is used (e.g. automatically converting UDF + arguments). Clients using sqlite3_value_to_pointer(), and its + related APIs, will need to manage those themselves. + */ + capi.sqlite3_value_to_js = function(pVal, throwIfCannotConvert = true) { + let arg; + const valType = capi.sqlite3_value_type(pVal); + switch (valType) { + case capi.SQLITE_INTEGER: + if (wasm.bigIntEnabled) { + arg = capi.sqlite3_value_int64(pVal); + if (util.bigIntFitsDouble(arg)) arg = Number(arg); + } else arg = capi.sqlite3_value_double(pVal); + break; + case capi.SQLITE_FLOAT: + arg = capi.sqlite3_value_double(pVal); + break; + case capi.SQLITE_TEXT: + arg = capi.sqlite3_value_text(pVal); + break; + case capi.SQLITE_BLOB: { + const n = capi.sqlite3_value_bytes(pVal); + const pBlob = capi.sqlite3_value_blob(pVal); + if (n && !pBlob) sqlite3.WasmAllocError.toss("Cannot allocate memory for blob argument of", n, "byte(s)"); + arg = n ? wasm.heap8u().slice(Number(pBlob), Number(pBlob) + Number(n)) : null; + break; + } + case capi.SQLITE_NULL: + arg = null; + break; + default: + if (throwIfCannotConvert) toss3(capi.SQLITE_MISMATCH, "Unhandled sqlite3_value_type():", valType); + arg = void 0; + } + return arg; + }; + /** + Requires a C-style array of `sqlite3_value*` objects and the + number of entries in that array. Returns a JS array containing + the results of passing each C array entry to + sqlite3_value_to_js(). The 3rd argument to this function is + passed on as the 2nd argument to that one. + */ + capi.sqlite3_values_to_js = function(argc, pArgv, throwIfCannotConvert = true) { + let i; + const tgt = []; + for (i = 0; i < argc; ++i) + /** + Curiously: despite ostensibly requiring 8-byte + alignment, the pArgv array is parcelled into chunks of + 4 bytes (1 pointer each). The values those point to + have 8-byte alignment but the individual argv entries + do not. + */ + tgt.push(capi.sqlite3_value_to_js(wasm.peekPtr(wasm.ptr.add(pArgv, wasm.ptr.size * i)), throwIfCannotConvert)); + return tgt; + }; + /** + Calls either sqlite3_result_error_nomem(), if e is-a + WasmAllocError, or sqlite3_result_error(). In the latter case, + the second argument is coerced to a string to create the error + message. + + The first argument is a (sqlite3_context*). Returns void. + Does not throw. + */ + capi.sqlite3_result_error_js = function(pCtx, e) { + if (e instanceof WasmAllocError) capi.sqlite3_result_error_nomem(pCtx); + else capi.sqlite3_result_error(pCtx, "" + e, -1); + }; + /** + This function passes its 2nd argument to one of the + sqlite3_result_xyz() routines, depending on the type of that + argument: + + - If (val instanceof Error), this function passes it to + sqlite3_result_error_js(). + - `null`: `sqlite3_result_null()` + - `boolean`: `sqlite3_result_int()` with a value of 0 or 1. + - `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or + `sqlite3_result_double()`, depending on the range of the number + and whether or not int64 support is enabled. + - `bigint`: similar to `number` but will trigger an error if the + value is too big to store in an int64. + - `string`: `sqlite3_result_text()` + - Uint8Array or Int8Array or ArrayBuffer: `sqlite3_result_blob()` + - `undefined`: is a no-op provided to simplify certain use cases. + + Anything else triggers `sqlite3_result_error()` with a + description of the problem. + + The first argument to this function is a `(sqlite3_context*)`. + Returns void. Does not throw. + */ + capi.sqlite3_result_js = function(pCtx, val) { + if (val instanceof Error) { + capi.sqlite3_result_error_js(pCtx, val); + return; + } + try { + switch (typeof val) { + case "undefined": break; + case "boolean": + capi.sqlite3_result_int(pCtx, val ? 1 : 0); + break; + case "bigint": + if (util.bigIntFits32(val)) capi.sqlite3_result_int(pCtx, Number(val)); + else if (util.bigIntFitsDouble(val)) capi.sqlite3_result_double(pCtx, Number(val)); + else if (wasm.bigIntEnabled) if (util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); + else toss3("BigInt value", val.toString(), "is too BigInt for int64."); + else toss3("BigInt value", val.toString(), "is too BigInt."); + break; + case "number": { + let f; + if (util.isInt32(val)) f = capi.sqlite3_result_int; + else if (wasm.bigIntEnabled && Number.isInteger(val) && util.bigIntFits64(BigInt(val))) f = capi.sqlite3_result_int64; + else f = capi.sqlite3_result_double; + f(pCtx, val); + break; + } + case "string": { + const [p, n] = wasm.allocCString(val, true); + capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC); + break; + } + case "object": if (null === val) { + capi.sqlite3_result_null(pCtx); + break; + } else if (util.isBindableTypedArray(val)) { + const pBlob = wasm.allocFromTypedArray(val); + capi.sqlite3_result_blob(pCtx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); + break; + } + default: toss3("Don't not how to handle this UDF result value:", typeof val, val); + } + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + /** + Returns the result sqlite3_column_value(pStmt,iCol) passed to + sqlite3_value_to_js(). The 3rd argument of this function is + ignored by this function except to pass it on as the second + argument of sqlite3_value_to_js(). If the sqlite3_column_value() + returns NULL (e.g. because the column index is out of range), + this function returns `undefined`, regardless of the 3rd + argument. If the 3rd argument is falsy and conversion fails, + `undefined` will be returned. + + Note that sqlite3_column_value() returns an "unprotected" value + object, but in a single-threaded environment (like this one) + there is no distinction between protected and unprotected values. + */ + capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert = true) { + const v = capi.sqlite3_column_value(pStmt, iCol); + return 0 === v ? void 0 : capi.sqlite3_value_to_js(v, throwIfCannotConvert); + }; + { + /** + Internal impl of sqlite3_preupdate_new/old_js() and + sqlite3changeset_new/old_js(). + */ + const __newOldValue = function(pObj, iCol, impl) { + impl = capi[impl]; + if (!this.ptr) this.ptr = wasm.allocPtr(); + else wasm.pokePtr(this.ptr, 0); + const rc = impl(pObj, iCol, this.ptr); + if (rc) return SQLite3Error.toss(rc, arguments[2] + "() failed with code " + rc); + const pv = wasm.peekPtr(this.ptr); + return pv ? capi.sqlite3_value_to_js(pv, true) : void 0; + }.bind(Object.create(null)); + /** + A wrapper around sqlite3_preupdate_new() which fetches the + sqlite3_value at the given index and returns the result of + passing it to sqlite3_value_to_js(). Throws on error. + */ + capi.sqlite3_preupdate_new_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_new"); + /** + The sqlite3_preupdate_old() counterpart of + sqlite3_preupdate_new_js(), with an identical interface. + */ + capi.sqlite3_preupdate_old_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_old"); + /** + A wrapper around sqlite3changeset_new() which fetches the + sqlite3_value at the given index and returns the result of + passing it to sqlite3_value_to_js(). Throws on error. + + If sqlite3changeset_new() succeeds but has no value to report, + this function returns the undefined value, noting that + undefined is not a valid conversion from an `sqlite3_value`, so + is unambiguous. + */ + capi.sqlite3changeset_new_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_new"); + /** + The sqlite3changeset_old() counterpart of + sqlite3changeset_new_js(), with an identical interface. + */ + capi.sqlite3changeset_old_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_old"); + } + const sqlite3 = { + WasmAllocError, + SQLite3Error, + capi, + util, + wasm, + config, + version: Object.create(null), + client: void 0, + asyncPostInit: async function ff() { + if (ff.isReady instanceof Promise) return ff.isReady; + let lia = this.initializersAsync; + delete this.initializersAsync; + const postInit = async () => { + if (!sqlite3.__isUnderTest) { + delete sqlite3.util; + delete sqlite3.StructBinder; + } + return sqlite3; + }; + const catcher = (e) => { + config.error("an async sqlite3 initializer failed:", e); + throw e; + }; + if (!lia || !lia.length) return ff.isReady = postInit().catch(catcher); + lia = lia.map((f) => { + return f instanceof Function ? async (x) => f(sqlite3) : f; + }); + lia.push(postInit); + let p = Promise.resolve(sqlite3); + while (lia.length) p = p.then(lia.shift()); + return ff.isReady = p.catch(catcher); + }.bind(sqlite3ApiBootstrap), + scriptInfo: void 0 + }; + if ("undefined" !== typeof sqlite3IsUnderTest) sqlite3.__isUnderTest = !!sqlite3IsUnderTest; + try { + sqlite3ApiBootstrap.initializers.forEach((f) => { + f(sqlite3); + }); + } catch (e) { + console.error("sqlite3 bootstrap initializer threw:", e); + throw e; + } + delete sqlite3ApiBootstrap.initializers; + sqlite3ApiBootstrap.sqlite3 = sqlite3; + if ("undefined" !== typeof sqlite3InitScriptInfo) { + sqlite3InitScriptInfo.debugModule("sqlite3ApiBootstrap() complete", sqlite3); + sqlite3.scriptInfo = sqlite3InitScriptInfo; + } + if (sqlite3.__isUnderTest) { + if ("undefined" !== typeof EmscriptenModule) sqlite3.config.emscripten = EmscriptenModule; + const iw = sqlite3.scriptInfo?.instantiateWasm; + if (iw) { + sqlite3.wasm.module = iw.module; + sqlite3.wasm.instance = iw.instance; + sqlite3.wasm.imports = iw.imports; + } + } + /** + Eliminate any confusion about whether these config objects may + be used after library initialization by eliminating the outward-facing + objects... + */ + delete globalThis.sqlite3ApiConfig; + delete globalThis.sqlite3ApiBootstrap; + delete sqlite3ApiBootstrap.defaultConfig; + return sqlite3.asyncPostInit().then((s) => { + if ("undefined" !== typeof sqlite3InitScriptInfo) sqlite3InitScriptInfo.debugModule("sqlite3.asyncPostInit() complete", s); + delete s.asyncPostInit; + delete s.scriptInfo; + delete s.emscripten; + return s; + }); + }; + /** + globalThis.sqlite3ApiBootstrap.initializers is an internal detail + used by the various pieces of the sqlite3 API's amalgamation + process. It must not be modified by client code except when plugging + such code into the amalgamation process. + + Each component of the amalgamation is expected to append a function + to this array. When sqlite3ApiBootstrap() is called for the first + time, each such function will be called (in their appended order) + and passed the sqlite3 namespace object, into which they can install + their features. At the end of that process, this array is deleted. + + The order of insertion into this array is significant for + some pieces. e.g. sqlite3.capi and sqlite3.wasm cannot be fully + utilized until the whwasmutil.js part is plugged in via + sqlite3-api-glue.js. + */ + globalThis.sqlite3ApiBootstrap.initializers = []; + /** + globalThis.sqlite3ApiBootstrap.initializersAsync is an internal detail + used by the sqlite3 API's amalgamation process. It must not be + modified by client code except when plugging such code into the + amalgamation process. + + The counterpart of globalThis.sqlite3ApiBootstrap.initializers, + specifically for initializers which are asynchronous. All entries in + this list must be either async functions, non-async functions which + return a Promise, or a Promise. Each function in the list is called + with the sqlite3 object as its only argument. + + The resolved value of any Promise is ignored and rejection will kill + the asyncPostInit() process (at an indeterminate point because all + of them are run asynchronously in parallel). + + This list is not processed until the client calls + sqlite3.asyncPostInit(). This means, for example, that intializers + added to globalThis.sqlite3ApiBootstrap.initializers may push entries to + this list. + */ + globalThis.sqlite3ApiBootstrap.initializersAsync = []; + /** + Client code may assign sqlite3ApiBootstrap.defaultConfig an + object-type value before calling sqlite3ApiBootstrap() (without + arguments) in order to tell that call to use this object as its + default config value. The intention of this is to provide + downstream clients with a reasonably flexible approach for plugging in + an environment-suitable configuration without having to define a new + global-scope symbol. + */ + globalThis.sqlite3ApiBootstrap.defaultConfig = Object.create(null); + /** + Placeholder: gets installed by the first call to + globalThis.sqlite3ApiBootstrap(). However, it is recommended that the + caller of sqlite3ApiBootstrap() capture its return value and delete + globalThis.sqlite3ApiBootstrap after calling it. It returns the same + value which will be stored here. + */ + globalThis.sqlite3ApiBootstrap.sqlite3 = void 0; + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + sqlite3.version = { + "libVersion": "3.52.0", + "libVersionNumber": 3052e3, + "sourceId": "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", + "downloadVersion": 352e4, + "scm": { + "sha3-256": "407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", + "branch": "trunk", + "tags": "", + "datetime": "2026-01-30T06:37:34.096Z" + } + }; + }); + globalThis.WhWasmUtilInstaller = function WhWasmUtilInstaller(target) { + "use strict"; + if (void 0 === target.bigIntEnabled) target.bigIntEnabled = !!globalThis["BigInt64Array"]; + /** Throws a new Error, the message of which is the concatenation of + all args with a space between each. */ + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + if (!target.pointerSize && !target.pointerIR && target.alloc && target.dealloc) { + const ptr = target.alloc(1); + target.pointerSize = "bigint" === typeof ptr ? 8 : 4; + target.dealloc(ptr); + } + /** + As of 2025-09-21, this library works with 64-bit WASM modules + built with Emscripten's -sMEMORY64=1. + */ + if (target.pointerSize && !target.pointerIR) target.pointerIR = 4 === target.pointerSize ? "i32" : "i64"; + const __ptrIR = target.pointerIR ??= "i32"; + const __ptrSize = target.pointerSize ??= "i32" === __ptrIR ? 4 : "i64" === __ptrIR ? 8 : 0; + delete target.pointerSize; + delete target.pointerIR; + if ("i32" !== __ptrIR && "i64" !== __ptrIR) toss("Invalid pointerIR:", __ptrIR); + else if (8 !== __ptrSize && 4 !== __ptrSize) toss("Invalid pointerSize:", __ptrSize); + /** Either BigInt or, if !target.bigIntEnabled, a function which + throws complaining that BigInt is not enabled. */ + const __BigInt = target.bigIntEnabled ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); + const __Number = (v) => Number(v || 0); + /** + If target.ptr.ir==='i32' then this is equivalent to + Number(v||0) else it's equivalent to BigInt(v||0), throwing + if BigInt support is disabled. + + Why? Because Number(null)===0, but BigInt(null) throws. We + perform the same for Number to allow the undefined value to be + treated as a NULL WASM pointer, primarily to reduce friction in + many SQLite3 bindings which have long relied on that. + */ + const __asPtrType = 4 === __ptrSize ? __Number : __BigInt; + /** + The number 0 as either type Number or BigInt, depending on + target.ptr.ir. + */ + const __NullPtr = __asPtrType(0); + /** + Expects any number of numeric arguments, each one of either type + Number or BigInt. It sums them up (from an implicit starting + point of 0 or 0n) and returns them as a number of the same type + which target.ptr.coerce() uses. + + This is a workaround for not being able to mix Number/BigInt in + addition/subtraction expressions (which we frequently need for + calculating pointer offsets). + */ + const __ptrAdd = function(...args) { + let rc = __asPtrType(0); + for (const v of args) rc += __asPtrType(v); + return rc; + }; + /** Set up target.ptr... */ + { + const __ptr = Object.create(null); + Object.defineProperty(target, "ptr", { + enumerable: true, + get: () => __ptr, + set: () => toss("The ptr property is read-only.") + }); + (function f(name, val) { + Object.defineProperty(__ptr, name, { + enumerable: true, + get: () => val, + set: () => toss("ptr[" + name + "] is read-only.") + }); + return f; + })("null", __NullPtr)("size", __ptrSize)("ir", __ptrIR)("coerce", __asPtrType)("add", __ptrAdd)("addn", 4 === __ptrIR ? __ptrAdd : (...args) => Number(__ptrAdd(...args))); + } + if (!target.exports) Object.defineProperty(target, "exports", { + enumerable: true, + configurable: true, + get: () => target.instance?.exports + }); + /** Stores various cached state. */ + const cache = Object.create(null); + /** Previously-recorded size of cache.memory.buffer, noted so that + we can recreate the view objects if the heap grows. */ + cache.heapSize = 0; + /** WebAssembly.Memory object extracted from target.memory or + target.exports.memory the first time heapWrappers() is + called. */ + cache.memory = null; + /** uninstallFunction() puts table indexes in here for reuse and + installFunction() extracts them. */ + cache.freeFuncIndexes = []; + /** + List-of-lists used by scopedAlloc() and friends. + */ + cache.scopedAlloc = []; + /** Push the pointer ptr to the current cache.scopedAlloc list + (which must already exist) and return ptr. */ + cache.scopedAlloc.pushPtr = (ptr) => { + cache.scopedAlloc[cache.scopedAlloc.length - 1].push(ptr); + return ptr; + }; + cache.utf8Decoder = new TextDecoder(); + cache.utf8Encoder = new TextEncoder("utf-8"); + /** + For the given IR-like string in the set ('i8', 'i16', 'i32', + 'f32', 'float', 'i64', 'f64', 'double', '*'), or any string value + ending in '*', returns the sizeof for that value + (target.ptr.size in the latter case). For any other value, it + returns the undefined value. + */ + target.sizeofIR = (n) => { + switch (n) { + case "i8": return 1; + case "i16": return 2; + case "i32": + case "f32": + case "float": return 4; + case "i64": + case "f64": + case "double": return 8; + case "*": return __ptrSize; + default: return ("" + n).endsWith("*") ? __ptrSize : void 0; + } + }; + /** + If (cache.heapSize !== cache.memory.buffer.byteLength), i.e. if + the heap has grown since the last call, updates cache.HEAPxyz. + Returns the cache object. + */ + const heapWrappers = function() { + if (!cache.memory) cache.memory = target.memory instanceof WebAssembly.Memory ? target.memory : target.exports.memory; + else if (cache.heapSize === cache.memory.buffer.byteLength) return cache; + const b = cache.memory.buffer; + cache.HEAP8 = new Int8Array(b); + cache.HEAP8U = new Uint8Array(b); + cache.HEAP16 = new Int16Array(b); + cache.HEAP16U = new Uint16Array(b); + cache.HEAP32 = new Int32Array(b); + cache.HEAP32U = new Uint32Array(b); + cache.HEAP32F = new Float32Array(b); + cache.HEAP64F = new Float64Array(b); + if (target.bigIntEnabled) if ("undefined" !== typeof BigInt64Array) { + cache.HEAP64 = new BigInt64Array(b); + cache.HEAP64U = new BigUint64Array(b); + } else toss("BigInt support is enabled, but the BigInt64Array type is missing."); + cache.heapSize = b.byteLength; + return cache; + }; + /** Convenience equivalent of this.heapForSize(8,false). */ + target.heap8 = () => heapWrappers().HEAP8; + /** Convenience equivalent of this.heapForSize(8,true). */ + target.heap8u = () => heapWrappers().HEAP8U; + /** Convenience equivalent of this.heapForSize(16,false). */ + target.heap16 = () => heapWrappers().HEAP16; + /** Convenience equivalent of this.heapForSize(16,true). */ + target.heap16u = () => heapWrappers().HEAP16U; + /** Convenience equivalent of this.heapForSize(32,false). */ + target.heap32 = () => heapWrappers().HEAP32; + /** Convenience equivalent of this.heapForSize(32,true). */ + target.heap32u = () => heapWrappers().HEAP32U; + /** + Requires n to be one of: + + - integer 8, 16, or 32. + - A integer-type TypedArray constructor: Int8Array, Int16Array, + Int32Array, or their Uint counterparts. + + If this.bigIntEnabled is true, it also accepts the value 64 or a + BigInt64Array/BigUint64Array, else it throws if passed 64 or one + of those constructors. + + Returns an integer-based TypedArray view of the WASM heap memory + buffer associated with the given block size. If passed an integer + as the first argument and unsigned is truthy then the "U" + (unsigned) variant of that view is returned, else the signed + variant is returned. If passed a TypedArray value, the 2nd + argument is ignored. Float32Array and Float64Array views are not + supported by this function. + + Growth of the heap will invalidate any references to this heap, + so do not hold a reference longer than needed and do not use a + reference after any operation which may allocate. Instead, + re-fetch the reference by calling this function again. + + Throws if passed an invalid n. + */ + target.heapForSize = function(n, unsigned = true) { + const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); + switch (n) { + case Int8Array: return c.HEAP8; + case Uint8Array: return c.HEAP8U; + case Int16Array: return c.HEAP16; + case Uint16Array: return c.HEAP16U; + case Int32Array: return c.HEAP32; + case Uint32Array: return c.HEAP32U; + case 8: return unsigned ? c.HEAP8U : c.HEAP8; + case 16: return unsigned ? c.HEAP16U : c.HEAP16; + case 32: return unsigned ? c.HEAP32U : c.HEAP32; + case 64: + if (c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64; + break; + default: if (target.bigIntEnabled) { + if (n === globalThis["BigUint64Array"]) return c.HEAP64U; + else if (n === globalThis["BigInt64Array"]) return c.HEAP64; + break; + } + } + toss("Invalid heapForSize() size: expecting 8, 16, 32,", "or (if BigInt is enabled) 64."); + }; + const __funcTable = target.functionTable; + delete target.functionTable; + /** + Returns the WASM-exported "indirect function table". + */ + target.functionTable = __funcTable ? () => __funcTable : () => target.exports.__indirect_function_table; + /** + Given a function pointer, returns the WASM function table entry + if found, else returns a falsy value: undefined if fptr is out of + range or null if it's in range but the table entry is empty. + */ + target.functionEntry = function(fptr) { + const ft = target.functionTable(); + return fptr < ft.length ? ft.get(__asPtrType(fptr)) : void 0; + }; + /** + Creates a WASM function which wraps the given JS function and + returns the JS binding of that WASM function. The signature + string must be the Jaccwabyt-format or Emscripten + addFunction()-format function signature string. In short: in may + have one of the following formats: + + - Emscripten: `"x..."`, where the first x is a letter representing + the result type and subsequent letters represent the argument + types. Functions with no arguments have only a single + letter. + + - Jaccwabyt: `"x(...)"` where `x` is the letter representing the + result type and letters in the parens (if any) represent the + argument types. Functions with no arguments use `x()`. + + Supported letters: + + - `i` = int32 + - `p` = int32 or int64 ("pointer"), depending on target.ptr.size + - `j` = int64 + - `f` = float32 + - `d` = float64 + - `v` = void, only legal for use as the result type + + It throws if an invalid signature letter is used. + + Jaccwabyt-format signatures support some additional letters which + have no special meaning here but (in this context) act as aliases + for other letters: + + - `s`, `P`: same as `p` + + Sidebar: this code is developed together with Jaccwabyt, thus the + support for its signature format. + + The arguments may be supplied in either order: (func,sig) or + (sig,func). + */ + target.jsFuncToWasm = function f(func, sig) { + /** Attribution: adapted up from Emscripten-generated glue code, + refactored primarily for efficiency's sake, eliminating + call-local functions and superfluous temporary arrays. */ + if (!f._) f._ = { + sigTypes: Object.assign(Object.create(null), { + i: "i32", + p: __ptrIR, + P: __ptrIR, + s: __ptrIR, + j: "i64", + f: "f32", + d: "f64" + }), + typeCodes: Object.assign(Object.create(null), { + f64: 124, + f32: 125, + i64: 126, + i32: 127 + }), + uleb128Encode: (tgt, method, n) => { + if (n < 128) tgt[method](n); + else tgt[method](n % 128 | 128, n >> 7); + }, + rxJSig: /^(\w)\((\w*)\)$/, + sigParams: (sig) => { + const m = f._.rxJSig.exec(sig); + return m ? m[2] : sig.substr(1); + }, + letterType: (x) => f._.sigTypes[x] || toss("Invalid signature letter:", x), + pushSigType: (dest, letter) => dest.push(f._.typeCodes[f._.letterType(letter)]) + }; + if ("string" === typeof func) { + const x = sig; + sig = func; + func = x; + } + const _ = f._; + const sigParams = _.sigParams(sig); + const wasmCode = [1, 96]; + _.uleb128Encode(wasmCode, "push", sigParams.length); + for (const x of sigParams) _.pushSigType(wasmCode, x); + if ("v" === sig[0]) wasmCode.push(0); + else { + wasmCode.push(1); + _.pushSigType(wasmCode, sig[0]); + } + _.uleb128Encode(wasmCode, "unshift", wasmCode.length); + wasmCode.unshift(0, 97, 115, 109, 1, 0, 0, 0, 1); + wasmCode.push(2, 7, 1, 1, 101, 1, 102, 0, 0, 7, 5, 1, 1, 102, 0, 0); + return new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(wasmCode)), { e: { f: func } }).exports["f"]; + }; + /** + Documented as target.installFunction() except for the 3rd + argument: if truthy, the newly-created function pointer + is stashed in the current scoped-alloc scope and will be + cleaned up at the matching scopedAllocPop(), else it + is not stashed there. + */ + const __installFunction = function f(func, sig, scoped) { + if (scoped && !cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); + if ("string" === typeof func) { + const x = sig; + sig = func; + func = x; + } + if ("string" !== typeof sig || !(func instanceof Function)) toss("Invalid arguments: expecting (function,signature) or (signature,function)."); + const ft = target.functionTable(); + const oldLen = __asPtrType(ft.length); + let ptr; + while (ptr = cache.freeFuncIndexes.pop()) if (ft.get(ptr)) { + ptr = null; + continue; + } else break; + if (!ptr) { + ptr = __asPtrType(oldLen); + ft.grow(__asPtrType(1)); + } + try { + ft.set(ptr, func); + if (scoped) cache.scopedAlloc.pushPtr(ptr); + return ptr; + } catch (e) { + if (!(e instanceof TypeError)) { + if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); + throw e; + } + } + try { + const fptr = target.jsFuncToWasm(func, sig); + ft.set(ptr, fptr); + if (scoped) cache.scopedAlloc.pushPtr(ptr); + } catch (e) { + if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); + throw e; + } + return ptr; + }; + /** + Expects a JS function and signature, exactly as for + this.jsFuncToWasm(). It uses that function to create a + WASM-exported function, installs that function to the next + available slot of this.functionTable(), and returns the + function's index in that table (which acts as a pointer to that + function). The returned pointer can be passed to + uninstallFunction() to uninstall it and free up the table slot + for reuse. + + If passed (string,function) arguments then it treats the first + argument as the signature and second as the function. + + As a special case, if the passed-in function is a WASM-exported + function then the signature argument is ignored and func is + installed as-is, without requiring re-compilation/re-wrapping. + + This function will propagate an exception if + WebAssembly.Table.grow() throws or this.jsFuncToWasm() throws. + The former case can happen in an Emscripten-compiled environment + when building without Emscripten's `-sALLOW_TABLE_GROWTH` flag. + + Sidebar: this function differs from Emscripten's addFunction() + _primarily_ in that it does not share that function's + undocumented behavior of reusing a function if it's passed to + addFunction() more than once, which leads to uninstallFunction() + breaking clients which do not take care to avoid that case: + + https://github.com/emscripten-core/emscripten/issues/17323 + */ + target.installFunction = (func, sig) => __installFunction(func, sig, false); + /** + Works exactly like installFunction() but requires that a + scopedAllocPush() is active and uninstalls the given function + when that alloc scope is popped via scopedAllocPop(). + This is used for implementing JS/WASM function bindings which + should only persist for the life of a call into a single + C-side function. + */ + target.scopedInstallFunction = (func, sig) => __installFunction(func, sig, true); + /** + Requires a pointer value previously returned from + this.installFunction(). Removes that function from the WASM + function table, marks its table slot as free for re-use, and + returns that function. It is illegal to call this before + installFunction() has been called and results are undefined if + ptr was not returned by that function. The returned function + may be passed back to installFunction() to reinstall it. + + To simplify certain use cases, if passed a falsy non-0 value + (noting that 0 is a valid function table index), this function + has no side effects and returns undefined. + */ + target.uninstallFunction = function(ptr) { + if (!ptr && __NullPtr !== ptr) return void 0; + const ft = target.functionTable(); + cache.freeFuncIndexes.push(ptr); + const rc = ft.get(ptr); + ft.set(ptr, null); + return rc; + }; + /** + Given a WASM heap memory address and a data type name in the form + (i8, i16, i32, i64, float (or f32), double (or f64)), this + fetches the numeric value from that address and returns it as a + number or, for the case of type='i64', a BigInt (with the caveat + BigInt will trigger an exception if this.bigIntEnabled is + falsy). Throws if given an invalid type. + + If the first argument is an array, it is treated as an array of + addresses and the result is an array of the values from each of + those address, using the same 2nd argument for determining the + value type to fetch. + + As a special case, if type ends with a `*`, it is considered to + be a pointer type and is treated as the WASM numeric type + appropriate for the pointer size (==this.ptr.ir). + + While possibly not obvious, this routine and its poke() + counterpart are how pointer-to-value _output_ parameters in + WASM-compiled C code can be interacted with: + + ``` + const ptr = alloc(4); + poke32(ptr, 0); // clear the ptr's value + aCFuncWithOutputPtrToInt32Arg(ptr); // e.g. void foo(int *x); + const result = peek32(ptr); // fetch ptr's value + dealloc(ptr); + ``` + + scopedAlloc() and friends can be used to make handling of + `ptr` safe against leaks in the case of an exception: + + ``` + let result; + const scope = scopedAllocPush(); + try{ + const ptr = scopedAlloc(4); + poke32(ptr, 0); + aCFuncWithOutputPtrArg(ptr); + result = peek32(ptr); + }finally{ + scopedAllocPop(scope); + } + ``` + + As a rule poke() must be called to set (typically zero out) the + pointer's value, else it will contain an essentially random + value. + + ACHTUNG: calling this often, e.g. in a loop, can have a noticably + painful impact on performance. Rather than doing so, use + heapForSize() to fetch the heap object and read directly from it. + + ACHTUNG #2: ptr may be a BigInt (and generally will be in 64-bit + builds) but this function must coerce it into a Number in order + to access the heap's contents. Ergo: BitInts outside of the + (extrardinarily genereous) address range exposed to browser-side + WASM may cause misbehavior. + + See also: poke() + */ + target.peek = function f(ptr, type = "i8") { + if (type.endsWith("*")) type = __ptrIR; + const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); + const list = Array.isArray(ptr) ? [] : void 0; + let rc; + do { + if (list) ptr = arguments[0].shift(); + switch (type) { + case "i1": + case "i8": + rc = c.HEAP8[Number(ptr) >> 0]; + break; + case "i16": + rc = c.HEAP16[Number(ptr) >> 1]; + break; + case "i32": + rc = c.HEAP32[Number(ptr) >> 2]; + break; + case "float": + case "f32": + rc = c.HEAP32F[Number(ptr) >> 2]; + break; + case "double": + case "f64": + rc = Number(c.HEAP64F[Number(ptr) >> 3]); + break; + case "i64": if (c.HEAP64) { + rc = __BigInt(c.HEAP64[Number(ptr) >> 3]); + break; + } + default: toss("Invalid type for peek():", type); + } + if (list) list.push(rc); + } while (list && arguments[0].length); + return list || rc; + }; + /** + The counterpart of peek(), this sets a numeric value at the given + WASM heap address, using the 3rd argument to define how many + bytes are written. Throws if given an invalid type. See peek() + for details about the `type` argument. If the 3rd argument ends + with `*` then it is treated as a pointer type and this function + behaves as if the 3rd argument were this.ptr.ir. + + If the first argument is an array, it is treated like a list + of pointers and the given value is written to each one. + + Returns `this`. (Prior to 2022-12-09 it returned this function.) + + ACHTUNG #1: see peek()'s ACHTUNG #1. + + ACHTUNG #2: see peek()'s ACHTUNG #2. + */ + target.poke = function(ptr, value, type = "i8") { + if (type.endsWith("*")) type = __ptrIR; + const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); + for (const p of Array.isArray(ptr) ? ptr : [ptr]) switch (type) { + case "i1": + case "i8": + c.HEAP8[Number(p) >> 0] = value; + continue; + case "i16": + c.HEAP16[Number(p) >> 1] = value; + continue; + case "i32": + c.HEAP32[Number(p) >> 2] = value; + continue; + case "float": + case "f32": + c.HEAP32F[Number(p) >> 2] = value; + continue; + case "double": + case "f64": + c.HEAP64F[Number(p) >> 3] = value; + continue; + case "i64": if (c.HEAP64) { + c.HEAP64[Number(p) >> 3] = __BigInt(value); + continue; + } + default: toss("Invalid type for poke(): " + type); + } + return this; + }; + /** + Convenience form of peek() intended for fetching + pointer-to-pointer values. If passed a single non-array argument + it returns the value of that one pointer address. If passed + multiple arguments, or a single array of arguments, it returns an + array of their values. + */ + target.peekPtr = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, __ptrIR); + /** + A variant of poke() intended for setting pointer-to-pointer + values. Its differences from poke() are that (1) it defaults to a + value of 0 and (2) it always writes to the pointer-sized heap + view. + */ + target.pokePtr = (ptr, value = 0) => target.poke(ptr, value, __ptrIR); + /** + Convenience form of peek() intended for fetching i8 values. If + passed a single non-array argument it returns the value of that + one pointer address. If passed multiple arguments, or a single + array of arguments, it returns an array of their values. + */ + target.peek8 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i8"); + /** + Convenience form of poke() intended for setting individual bytes. + Its difference from poke() is that it always writes to the + i8-sized heap view. + */ + target.poke8 = (ptr, value) => target.poke(ptr, value, "i8"); + /** i16 variant of peek8(). */ + target.peek16 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i16"); + /** i16 variant of poke8(). */ + target.poke16 = (ptr, value) => target.poke(ptr, value, "i16"); + /** i32 variant of peek8(). */ + target.peek32 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i32"); + /** i32 variant of poke8(). */ + target.poke32 = (ptr, value) => target.poke(ptr, value, "i32"); + /** i64 variant of peek8(). Will throw if this build is not + configured for BigInt support. */ + target.peek64 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i64"); + /** i64 variant of poke8(). Will throw if this build is not + configured for BigInt support. Note that this returns + a BigInt-type value, not a Number-type value. */ + target.poke64 = (ptr, value) => target.poke(ptr, value, "i64"); + /** f32 variant of peek8(). */ + target.peek32f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f32"); + /** f32 variant of poke8(). */ + target.poke32f = (ptr, value) => target.poke(ptr, value, "f32"); + /** f64 variant of peek8(). */ + target.peek64f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f64"); + /** f64 variant of poke8(). */ + target.poke64f = (ptr, value) => target.poke(ptr, value, "f64"); + /** Deprecated alias for getMemValue() */ + target.getMemValue = target.peek; + /** Deprecated alias for peekPtr() */ + target.getPtrValue = target.peekPtr; + /** Deprecated alias for poke() */ + target.setMemValue = target.poke; + /** Deprecated alias for pokePtr() */ + target.setPtrValue = target.pokePtr; + /** + Returns true if the given value appears to be legal for use as + a WASM pointer value. Its _range_ of values is not (cannot be) + validated except to ensure that it is a 32-bit integer with a + value of 0 or greater. Likewise, it cannot verify whether the + value actually refers to allocated memory in the WASM heap. + + Whether or not null or undefined are legal are context-dependent. + They generally are legal but this function does not treat them as + such because they're not strictly legal for passing as-is as WASM + integer arguments. + */ + target.isPtr32 = (ptr) => "number" === typeof ptr && ptr >= 0 && ptr === (ptr | 0); + /** 64-bit counterpart of isPtr32() and falls back to that function + if ptr is not a BigInt. */ + target.isPtr64 = (ptr) => "bigint" === typeof ptr ? ptr >= 0 : target.isPtr32(ptr); + /** + isPtr() is an alias for isPtr32() or isPtr64(), depending on the + value of target.ptr.size. + */ + target.isPtr = 4 === __ptrSize ? target.isPtr32 : target.isPtr64; + /** + Expects ptr to be a pointer into the WASM heap memory which + refers to a NUL-terminated C-style string encoded as UTF-8. + Returns the length, in bytes, of the string, as for `strlen(3)`. + As a special case, if !ptr or if it's not a pointer then it + returns `null`. Throws if ptr is out of range for + target.heap8u(). + */ + target.cstrlen = function(ptr) { + if (!ptr || !target.isPtr(ptr)) return null; + ptr = Number(ptr); + const h = heapWrappers().HEAP8U; + let pos = ptr; + for (; h[pos] !== 0; ++pos); + return pos - ptr; + }; + /** Internal helper to use in operations which need to distinguish + between TypedArrays which are backed by a SharedArrayBuffer + from those which are not. */ + const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; + /** Returns true if the given TypedArray object is backed by a + SharedArrayBuffer, else false. */ + const isSharedTypedArray = (aTypedArray) => aTypedArray.buffer instanceof __SAB; + target.isSharedTypedArray = isSharedTypedArray; + /** + Returns either aTypedArray.slice(begin,end) (if + aTypedArray.buffer is a SharedArrayBuffer) or + aTypedArray.subarray(begin,end) (if it's not). + + This distinction is important for APIs which don't like to + work on SABs, e.g. TextDecoder, and possibly for our + own APIs which work on memory ranges which "might" be + modified by other threads while they're working. + + begin and end may be of type Number or (in 64-bit builds) BigInt + (which get coerced to Numbers). + */ + const typedArrayPart = (aTypedArray, begin, end) => { + if (8 === __ptrSize) { + if ("bigint" === typeof begin) begin = Number(begin); + if ("bigint" === typeof end) end = Number(end); + } + return isSharedTypedArray(aTypedArray) ? aTypedArray.slice(begin, end) : aTypedArray.subarray(begin, end); + }; + target.typedArrayPart = typedArrayPart; + /** + Uses TextDecoder to decode the given half-open range of the given + TypedArray to a string. This differs from a simple call to + TextDecoder in that it accounts for whether the first argument is + backed by a SharedArrayBuffer or not, and can work more + efficiently if it's not (TextDecoder refuses to act upon an SAB). + + If begin/end are not provided or are falsy then each defaults to + the start/end of the array. + */ + const typedArrayToString = (typedArray, begin, end) => cache.utf8Decoder.decode(typedArrayPart(typedArray, begin, end)); + target.typedArrayToString = typedArrayToString; + /** + Expects ptr to be a pointer into the WASM heap memory which + refers to a NUL-terminated C-style string encoded as UTF-8. This + function counts its byte length using cstrlen() then returns a + JS-format string representing its contents. As a special case, if + ptr is falsy or not a pointer, `null` is returned. + */ + target.cstrToJs = function(ptr) { + const n = target.cstrlen(ptr); + return n ? typedArrayToString(heapWrappers().HEAP8U, Number(ptr), Number(ptr) + n) : null === n ? n : ""; + }; + /** + Given a JS string, this function returns its UTF-8 length in + bytes. Returns null if str is not a string. + */ + target.jstrlen = function(str) { + /** Attribution: derived from Emscripten's lengthBytesUTF8() */ + if ("string" !== typeof str) return null; + const n = str.length; + let len = 0; + for (let i = 0; i < n; ++i) { + let u = str.charCodeAt(i); + if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023; + if (u <= 127) ++len; + else if (u <= 2047) len += 2; + else if (u <= 65535) len += 3; + else len += 4; + } + return len; + }; + /** + Encodes the given JS string as UTF8 into the given TypedArray + tgt, starting at the given offset and writing, at most, maxBytes + bytes (including the NUL terminator if addNul is true, else no + NUL is added). If it writes any bytes at all and addNul is true, + it always NUL-terminates the output, even if doing so means that + the NUL byte is all that it writes. + + If maxBytes is negative (the default) then it is treated as the + remaining length of tgt, starting at the given offset. + + If writing the last character would surpass the maxBytes count + because the character is multi-byte, that character will not be + written (as opposed to writing a truncated multi-byte character). + This can lead to it writing as many as 3 fewer bytes than + maxBytes specifies. + + Returns the number of bytes written to the target, _including_ + the NUL terminator (if any). If it returns 0, it wrote nothing at + all, which can happen if: + + - str is empty and addNul is false. + - offset < 0. + - maxBytes == 0. + - maxBytes is less than the byte length of a multi-byte str[0]. + + Throws if tgt is not an Int8Array or Uint8Array. + + Design notes: + + - In C's strcpy(), the destination pointer is the first + argument. That is not the case here primarily because the 3rd+ + arguments are all referring to the destination, so it seems to + make sense to have them grouped with it. + + - Emscripten's counterpart of this function (stringToUTF8Array()) + returns the number of bytes written sans NUL terminator. That + is, however, ambiguous: str.length===0 or maxBytes===(0 or 1) + all cause 0 to be returned. + */ + target.jstrcpy = function(jstr, tgt, offset = 0, maxBytes = -1, addNul = true) { + /** Attribution: the encoding bits are taken from Emscripten's + stringToUTF8Array(). */ + if (!tgt || !(tgt instanceof Int8Array) && !(tgt instanceof Uint8Array)) toss("jstrcpy() target must be an Int8Array or Uint8Array."); + maxBytes = Number(maxBytes); + offset = Number(offset); + if (maxBytes < 0) maxBytes = tgt.length - offset; + if (!(maxBytes > 0) || !(offset >= 0)) return 0; + let i = 0, max = jstr.length; + const begin = offset, end = offset + maxBytes - (addNul ? 1 : 0); + for (; i < max && offset < end; ++i) { + let u = jstr.charCodeAt(i); + if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | jstr.charCodeAt(++i) & 1023; + if (u <= 127) { + if (offset >= end) break; + tgt[offset++] = u; + } else if (u <= 2047) { + if (offset + 1 >= end) break; + tgt[offset++] = 192 | u >> 6; + tgt[offset++] = 128 | u & 63; + } else if (u <= 65535) { + if (offset + 2 >= end) break; + tgt[offset++] = 224 | u >> 12; + tgt[offset++] = 128 | u >> 6 & 63; + tgt[offset++] = 128 | u & 63; + } else { + if (offset + 3 >= end) break; + tgt[offset++] = 240 | u >> 18; + tgt[offset++] = 128 | u >> 12 & 63; + tgt[offset++] = 128 | u >> 6 & 63; + tgt[offset++] = 128 | u & 63; + } + } + if (addNul) tgt[offset++] = 0; + return offset - begin; + }; + /** + Works similarly to C's strncpy(), copying, at most, n bytes (not + characters) from srcPtr to tgtPtr. It copies until n bytes have + been copied or a 0 byte is reached in src. _Unlike_ strncpy(), it + returns the number of bytes it assigns in tgtPtr, _including_ the + NUL byte (if any). If n is reached before a NUL byte in srcPtr, + tgtPtr will _not_ be NULL-terminated. If a NUL byte is reached + before n bytes are copied, tgtPtr will be NUL-terminated. + + If n is negative, cstrlen(srcPtr)+1 is used to calculate it, the + +1 being for the NUL byte. + + Throws if tgtPtr or srcPtr are falsy. Results are undefined if: + + - either is not a pointer into the WASM heap or + + - srcPtr is not NUL-terminated AND n is less than srcPtr's + logical length. + + ACHTUNG: it is possible to copy partial multi-byte characters + this way, and converting such strings back to JS strings will + have undefined results. + */ + target.cstrncpy = function(tgtPtr, srcPtr, n) { + if (!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings."); + if (n < 0) n = target.cstrlen(strPtr) + 1; + else if (!(n > 0)) return 0; + const heap = target.heap8u(); + let i = 0, ch; + const tgtNumber = Number(tgtPtr), srcNumber = Number(srcPtr); + for (; i < n && (ch = heap[srcNumber + i]); ++i) heap[tgtNumber + i] = ch; + if (i < n) heap[tgtNumber + i++] = 0; + return i; + }; + /** + For the given JS string, returns a Uint8Array of its contents + encoded as UTF-8. If addNul is true, the returned array will have + a trailing 0 entry, else it will not. + */ + target.jstrToUintArray = (str, addNul = false) => { + return cache.utf8Encoder.encode(addNul ? str + "\0" : str); + }; + const __affirmAlloc = (obj, funcName) => { + if (!(obj.alloc instanceof Function) || !(obj.dealloc instanceof Function)) toss("Object is missing alloc() and/or dealloc() function(s)", "required by", funcName + "()."); + }; + const __allocCStr = function(jstr, returnWithLength, allocator, funcName) { + __affirmAlloc(target, funcName); + if ("string" !== typeof jstr) return null; + const u = cache.utf8Encoder.encode(jstr), ptr = allocator(u.length + 1); + let toFree = ptr; + try { + const heap = heapWrappers().HEAP8U; + heap.set(u, Number(ptr)); + heap[__ptrAdd(ptr, u.length)] = 0; + toFree = __NullPtr; + return returnWithLength ? [ptr, u.length] : ptr; + } finally { + if (toFree) target.dealloc(toFree); + } + }; + /** + Uses target.alloc() to allocate enough memory for jstrlen(jstr)+1 + bytes of memory, copies jstr to that memory using jstrcpy(), + NUL-terminates it, and returns the pointer to that C-string. + Ownership of the pointer is transfered to the caller, who must + eventually pass the pointer to dealloc() to free it. + + If passed a truthy 2nd argument then its return semantics change: + it returns [ptr,n], where ptr is the C-string's pointer and n is + its cstrlen(). + + Throws if `target.alloc` or `target.dealloc` are not functions. + */ + target.allocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.alloc, "allocCString()"); + /** + Starts an "allocation scope." All allocations made using + scopedAlloc() are recorded in this scope and are freed when the + value returned from this function is passed to + scopedAllocPop(). + + This family of functions requires that the API's object have both + `alloc()` and `dealloc()` methods, else this function will throw. + + Intended usage: + + ``` + const scope = scopedAllocPush(); + try { + const ptr1 = scopedAlloc(100); + const ptr2 = scopedAlloc(200); + const ptr3 = scopedAlloc(300); + ... + // Note that only allocations made via scopedAlloc() + // are managed by this allocation scope. + }finally{ + scopedAllocPop(scope); + } + ``` + + The value returned by this function must be treated as opaque by + the caller, suitable _only_ for passing to scopedAllocPop(). + Its type and value are not part of this function's API and may + change in any given version of this code. + + `scopedAlloc.level` can be used to determine how many scoped + alloc levels are currently active. + */ + target.scopedAllocPush = function() { + __affirmAlloc(target, "scopedAllocPush"); + const a = []; + cache.scopedAlloc.push(a); + return a; + }; + /** + Cleans up all allocations made using scopedAlloc() in the context + of the given opaque state object, which must be a value returned + by scopedAllocPush(). See that function for an example of how to + use this function. It also uninstalls any WASM functions + installed with scopedInstallFunction(). + + Though scoped allocations are managed like a stack, this API + behaves properly if allocation scopes are popped in an order + other than the order they were pushed. The intent is that it + _always_ be used in a stack-like manner. + + If called with no arguments, it pops the most recent + scopedAllocPush() result: + + ``` + scopedAllocPush(); + try{ ... } finally { scopedAllocPop(); } + ``` + + It's generally recommended that it be passed an explicit argument + to help ensure that push/push are used in matching pairs, but in + trivial code that may be a non-issue. + */ + target.scopedAllocPop = function(state) { + __affirmAlloc(target, "scopedAllocPop"); + const n = arguments.length ? cache.scopedAlloc.indexOf(state) : cache.scopedAlloc.length - 1; + if (n < 0) toss("Invalid state object for scopedAllocPop()."); + if (0 === arguments.length) state = cache.scopedAlloc[n]; + cache.scopedAlloc.splice(n, 1); + for (let p; p = state.pop();) if (target.functionEntry(p)) target.uninstallFunction(p); + else target.dealloc(p); + }; + /** + Allocates n bytes of memory using this.alloc() and records that + fact in the state for the most recent call of scopedAllocPush(). + Ownership of the memory is given to scopedAllocPop(), which + will clean it up when it is called. The memory _must not_ be + passed to this.dealloc(). Throws if this API object is missing + the required `alloc()` or `dealloc()` functions or no scoped + alloc is active. + + See scopedAllocPush() for an example of how to use this function. + + The `level` property of this function can be queried to query how + many scoped allocation levels are currently active. + + See also: scopedAllocPtr(), scopedAllocCString() + */ + target.scopedAlloc = function(n) { + if (!cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); + const p = __asPtrType(target.alloc(n)); + return cache.scopedAlloc.pushPtr(p); + }; + Object.defineProperty(target.scopedAlloc, "level", { + configurable: false, + enumerable: false, + get: () => cache.scopedAlloc.length, + set: () => toss("The 'active' property is read-only.") + }); + /** + Works identically to allocCString() except that it allocates the + memory using scopedAlloc(). + + Will throw if no scopedAllocPush() call is active. + */ + target.scopedAllocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.scopedAlloc, "scopedAllocCString()"); + const __allocMainArgv = function(isScoped, list) { + const pList = target[isScoped ? "scopedAlloc" : "alloc"]((list.length + 1) * target.ptr.size); + let i = 0; + list.forEach((e) => { + target.pokePtr(__ptrAdd(pList, target.ptr.size * i++), target[isScoped ? "scopedAllocCString" : "allocCString"]("" + e)); + }); + target.pokePtr(__ptrAdd(pList, target.ptr.size * i), 0); + return pList; + }; + /** + Creates an array, using scopedAlloc(), suitable for passing to a + C-level main() routine. The input is a collection with a length + property and a forEach() method. A block of memory + (list.length+1) entries long is allocated and each pointer-sized + block of that memory is populated with a scopedAllocCString() + conversion of the (""+value) of each element, with the exception + that the final entry is a NULL pointer. Returns a pointer to the + start of the list, suitable for passing as the 2nd argument to a + C-style main() function. + + Throws if scopedAllocPush() is not active. + + Design note: the returned array is allocated with an extra NULL + pointer entry to accommodate certain APIs, but client code which + does not need that functionality should treat the returned array + as list.length entries long. + */ + target.scopedAllocMainArgv = (list) => __allocMainArgv(true, list); + /** + Identical to scopedAllocMainArgv() but uses alloc() instead of + scopedAlloc(). + */ + target.allocMainArgv = (list) => __allocMainArgv(false, list); + /** + Expects to be given a C-style string array and its length. It + returns a JS array of strings and/or nulls: any entry in the + pArgv array which is NULL results in a null entry in the result + array. If argc is 0 then an empty array is returned. + + Results are undefined if any entry in the first argc entries of + pArgv are neither 0 (NULL) nor legal UTF-format C strings. + + To be clear, the expected C-style arguments to be passed to this + function are `(int, char **)` (optionally const-qualified). + */ + target.cArgvToJs = (argc, pArgv) => { + const list = []; + for (let i = 0; i < argc; ++i) { + const arg = target.peekPtr(__ptrAdd(pArgv, target.ptr.size * i)); + list.push(arg ? target.cstrToJs(arg) : null); + } + return list; + }; + /** + Wraps function call func() in a scopedAllocPush() and + scopedAllocPop() block, such that all calls to scopedAlloc() and + friends from within that call will have their memory freed + automatically when func() returns. If func throws or propagates + an exception, the scope is still popped, otherwise it returns the + result of calling func(). + */ + target.scopedAllocCall = function(func) { + target.scopedAllocPush(); + try { + return func(); + } finally { + target.scopedAllocPop(); + } + }; + /** Internal impl for allocPtr() and scopedAllocPtr(). */ + const __allocPtr = function(howMany, safePtrSize, method) { + __affirmAlloc(target, method); + const pIr = safePtrSize ? "i64" : __ptrIR; + let m = target[method](howMany * (safePtrSize ? 8 : __ptrSize)); + target.poke(m, 0, pIr); + if (1 === howMany) return m; + const a = [m]; + for (let i = 1; i < howMany; ++i) { + m = __ptrAdd(m, safePtrSize ? 8 : __ptrSize); + a[i] = m; + target.poke(m, 0, pIr); + } + return a; + }; + /** + Allocates one or more pointers as a single chunk of memory and + zeroes them out. + + The first argument is the number of pointers to allocate. The + second specifies whether they should use a "safe" pointer size (8 + bytes) or whether they may use the default pointer size + (typically 4 but also possibly 8). + + How the result is returned depends on its first argument: if + passed 1, it returns the allocated memory address. If passed more + than one then an array of pointer addresses is returned, which + can optionally be used with "destructuring assignment" like this: + + ``` + const [p1, p2, p3] = allocPtr(3); + ``` + + ACHTUNG: when freeing the memory, pass only the _first_ result + value to dealloc(). The others are part of the same memory chunk + and must not be freed separately. + + The reason for the 2nd argument is... + + When one of the returned pointers will refer to a 64-bit value, + e.g. a double or int64, and that value must be written or fetched, + e.g. using poke() or peek(), it is important that + the pointer in question be aligned to an 8-byte boundary or else + it will not be fetched or written properly and will corrupt or + read neighboring memory. It is only safe to pass false when the + client code is certain that it will only get/fetch 4-byte values + (or smaller). + */ + target.allocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "alloc"); + /** + Identical to allocPtr() except that it allocates using scopedAlloc() + instead of alloc(). + */ + target.scopedAllocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "scopedAlloc"); + /** + If target.exports[name] exists, it is returned, else an + exception is thrown. + */ + target.xGet = function(name) { + return target.exports[name] || toss("Cannot find exported symbol:", name); + }; + const __argcMismatch = (f, n) => toss(f + "() requires", n, "argument(s)."); + /** + Looks up a WASM-exported function named fname from + target.exports. If found, it is called, passed all remaining + arguments, and its return value is returned to xCall's caller. If + not found, an exception is thrown. This function does no + conversion of argument or return types, but see xWrap() and + xCallWrapped() for variants which do. + + If the first argument is a function is is assumed to be + a WASM-bound function and is used as-is instead of looking up + the function via xGet(). + + As a special case, if passed only 1 argument after the name and + that argument in an Array, that array's entries become the + function arguments. (This is not an ambiguous case because it's + not legal to pass an Array object to a WASM function.) + */ + target.xCall = function(fname, ...args) { + const f = fname instanceof Function ? fname : target.xGet(fname); + if (!(f instanceof Function)) toss("Exported symbol", fname, "is not a function."); + if (f.length !== args.length) __argcMismatch(f === fname ? f.name : fname, f.length); + return 2 === arguments.length && Array.isArray(arguments[1]) ? f.apply(null, arguments[1]) : f.apply(null, args); + }; + /** + State for use with xWrap(). + */ + cache.xWrap = Object.create(null); + cache.xWrap.convert = Object.create(null); + /** Map of type names to argument conversion functions. */ + cache.xWrap.convert.arg = /* @__PURE__ */ new Map(); + /** Map of type names to return result conversion functions. */ + cache.xWrap.convert.result = /* @__PURE__ */ new Map(); + /** Scope-local convenience aliases. */ + const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result; + const __xArgPtr = __asPtrType; + xArg.set("i64", __BigInt).set("i32", (i) => i | 0).set("i16", (i) => (i | 0) & 65535).set("i8", (i) => (i | 0) & 255).set("f32", (i) => Number(i).valueOf()).set("float", xArg.get("f32")).set("f64", xArg.get("f32")).set("double", xArg.get("f64")).set("int", xArg.get("i32")).set("null", (i) => i).set(null, xArg.get("null")).set("**", __xArgPtr).set("*", __xArgPtr); + xResult.set("*", __xArgPtr).set("pointer", __xArgPtr).set("number", (v) => Number(v)).set("void", (v) => void 0).set(void 0, xResult.get("void")).set("null", (v) => v).set(null, xResult.get("null")); + for (const t of [ + "i8", + "i16", + "i32", + "i64", + "int", + "f32", + "float", + "f64", + "double" + ]) { + xArg.set(t + "*", __xArgPtr); + xResult.set(t + "*", __xArgPtr); + xResult.set(t, xArg.get(t) || toss("Maintenance required: missing arg converter for", t)); + } + /** + In order for args of type string to work in various contexts in + the sqlite3 API, we need to pass them on as, variably, a C-string + or a pointer value. Thus for ARGs of type 'string' and + '*'/'pointer' we behave differently depending on whether the + argument is a string or not: + + - If v is a string, scopeAlloc() a new C-string from it and return + that temp string's pointer. + + - Else return the value from the arg adapter defined for + target.ptr.ir. + + TODO? Permit an Int8Array/Uint8Array and convert it to a string? + Would that be too much magic concentrated in one place, ready to + backfire? We handle that at the client level in sqlite3 with a + custom argument converter. + */ + const __xArgString = (v) => { + return "string" === typeof v ? target.scopedAllocCString(v) : __asPtrType(v); + }; + xArg.set("string", __xArgString).set("utf8", __xArgString); + xResult.set("string", (i) => target.cstrToJs(i)).set("utf8", xResult.get("string")).set("string:dealloc", (i) => { + try { + return i ? target.cstrToJs(i) : null; + } finally { + target.dealloc(i); + } + }).set("utf8:dealloc", xResult.get("string:dealloc")).set("json", (i) => JSON.parse(target.cstrToJs(i))).set("json:dealloc", (i) => { + try { + return i ? JSON.parse(target.cstrToJs(i)) : null; + } finally { + target.dealloc(i); + } + }); + /** + Internal-use-only base class for FuncPtrAdapter and potentially + additional stateful argument adapter classes. + + Its main interface (convertArg()) is strictly internal, not to be + exposed to client code, as it may still need re-shaping. Only the + constructors of concrete subclasses should be exposed to clients, + and those in such a way that does not hinder internal redesign of + the convertArg() interface. + */ + const AbstractArgAdapter = class { + constructor(opt) { + this.name = opt.name || "unnamed adapter"; + } + /** + Gets called via xWrap() to "convert" v to whatever type + this specific class supports. + + argIndex is the argv index of _this_ argument in the + being-xWrap()'d call. argv is the current argument list + undergoing xWrap() argument conversion. argv entries to the + left of argIndex will have already undergone transformation and + those to the right will not have (they will have the values the + client-level code passed in, awaiting conversion). The RHS + indexes must never be relied upon for anything because their + types are indeterminate, whereas the LHS values will be + WASM-compatible values by the time this is called. + + The reason for the argv and argIndex arguments is that we + frequently need more context than v for a specific conversion, + and that context invariably lies in the LHS arguments of v. + Examples of how this is useful can be found in FuncPtrAdapter. + */ + convertArg(v, argv, argIndex) { + toss("AbstractArgAdapter must be subclassed."); + } + }; + /** + This type is recognized by xWrap() as a proxy for converting a JS + function to a C-side function, either permanently, for the + duration of a single call into the C layer, or semi-contextual, + where it may keep track of a single binding for a given context + and uninstall the binding if it's replaced. + + The constructor requires an options object with these properties: + + - name (optional): string describing the function binding. This + is solely for debugging and error-reporting purposes. If not + provided, an empty string is assumed. + + - signature: a function signature string compatible with + jsFuncToWasm(). + + - bindScope (string): one of ('transient', 'context', + 'singleton', 'permanent'). Bind scopes are: + + - 'transient': it will convert JS functions to WASM only for + the duration of the xWrap()'d function call, using + scopedInstallFunction(). Before that call returns, the + WASM-side binding will be uninstalled. + + - 'singleton': holds one function-pointer binding for this + instance. If it's called with a different function pointer, + it uninstalls the previous one after converting the new + value. This is only useful for use with "global" functions + which do not rely on any state other than this function + pointer. If the being-converted function pointer is intended + to be mapped to some sort of state object (e.g. an + `sqlite3*`) then "context" (see below) is the proper mode. + + - 'context': similar to singleton mode but for a given + "context", where the context is a key provided by the user + and possibly dependent on a small amount of call-time + context. This mode is the default if bindScope is _not_ set + but a property named contextKey (described below) is. + + - 'permanent': the function is installed and left there + forever. There is no way to recover its pointer address + later on for cleanup purposes. i.e. it effectively leaks. + + - callProxy (function): if set, this must be a function which + will act as a proxy for any "converted" JS function. It is + passed the being-converted function value and must return + either that function or a function which acts on its + behalf. The returned function will be the one which gets + installed into the WASM function table. The proxy must perform + any required argument conversion (it will be called from C + code, so will receive C-format arguments) before passing them + on to the being-converted function. Whether or not the proxy + itself must return a value depends on the context. If it does, + it must be a WASM-friendly value, as it will be returning from + a call made from WASM code. + + - contextKey (function): is only used if bindScope is 'context' + or if bindScope is not set and this function is, in which case + a bindScope of 'context' is assumed. This function gets bound + to this object, so its "this" is this object. It gets passed + (argv,argIndex), where argIndex is the index of _this_ function + in its _wrapping_ function's arguments, and argv is the + _current_ still-being-xWrap()-processed args array. (Got all + that?) When thisFunc(argv,argIndex) is called by xWrap(), all + arguments in argv to the left of argIndex will have been + processed by xWrap() by the time this is called. argv[argIndex] + will be the value the user passed in to the xWrap()'d function + for the argument this FuncPtrAdapter is mapped to. Arguments to + the right of argv[argIndex] will not yet have been converted + before this is called. The function must return a key which + uniquely identifies this function mapping context for _this_ + FuncPtrAdapter instance (other instances are not considered), + taking into account that C functions often take some sort of + state object as one or more of their arguments. As an example, + if the xWrap()'d function takes `(int,T*,functionPtr,X*)` then + this FuncPtrAdapter instance is argv[2], and contextKey(argv,2) + might return 'T@'+argv[1], or even just argv[1]. Note, + however, that the (`X*`) argument will not yet have been + processed by the time this is called and should not be used as + part of that key because its pre-conversion data type might be + unpredictable. Similarly, care must be taken with C-string-type + arguments: those to the left in argv will, when this is called, + be WASM pointers, whereas those to the right might (and likely + do) have another data type. When using C-strings in keys, never + use their pointers in the key because most C-strings in this + constellation are transient. Conversely, the pointer address + makes an ideal key for longer-lived native pointer types. + + Yes, that ^^^ is quite awkward, but it's what we have. In + context, as it were, it actually makes some sense, but one must + look under its hook a bit to understand why it's shaped the + way it is. + + The constructor only saves the above state for later, and does + not actually bind any functions. The conversion, if any, is + performed when its convertArg() method is called via xWrap(). + + Shortcomings: + + - These "reverse" bindings, i.e. calling into a JS-defined + function from a WASM-defined function (the generated proxy + wrapper), lack all type conversion support. That means, for + example, that... + + - Function pointers which include C-string arguments may still + need a level of hand-written wrappers around them, depending on + how they're used, in order to provide the client with JS + strings. Alternately, clients will need to perform such + conversions on their own, e.g. using cstrToJs(). The purpose of + the callProxy() method is to account for such cases. + */ + xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter { + constructor(opt) { + super(opt); + if (xArg.FuncPtrAdapter.warnOnUse) console.warn("xArg.FuncPtrAdapter is an internal-only API", "and is not intended to be invoked from", "client-level code. Invoked with:", opt); + this.name = opt.name || "unnamed"; + this.signature = opt.signature; + if (opt.contextKey instanceof Function) { + this.contextKey = opt.contextKey; + if (!opt.bindScope) opt.bindScope = "context"; + } + this.bindScope = opt.bindScope || toss("FuncPtrAdapter options requires a bindScope (explicit or implied)."); + if (FuncPtrAdapter.bindScopes.indexOf(opt.bindScope) < 0) toss("Invalid options.bindScope (" + opt.bindMod + ") for FuncPtrAdapter. Expecting one of: (" + FuncPtrAdapter.bindScopes.join(", ") + ")"); + this.isTransient = "transient" === this.bindScope; + this.isContext = "context" === this.bindScope; + this.isPermanent = "permanent" === this.bindScope; + this.singleton = "singleton" === this.bindScope ? [] : void 0; + this.callProxy = opt.callProxy instanceof Function ? opt.callProxy : void 0; + } + /** + The static class members are defined outside of the class to + work around an emcc toolchain build problem: one of the tools + in emsdk v3.1.42 does not support the static keyword. + */ + contextKey(argv, argIndex) { + return this; + } + /** + Returns this object's mapping for the given context key, in the + form of an an array, creating the mapping if needed. The key + may be anything suitable for use in a Map. + + The returned array is intended to be used as a pair of + [JSValue, WasmFuncPtr], where the first element is one passed + to this.convertArg() and the second is its WASM form. + */ + contextMap(key) { + const cm = this.__cmap || (this.__cmap = /* @__PURE__ */ new Map()); + let rc = cm.get(key); + if (void 0 === rc) cm.set(key, rc = []); + return rc; + } + /** + Gets called via xWrap() to "convert" v to a WASM-bound function + pointer. If v is one of (a WASM pointer, null, undefined) then + (v||0) is returned and any earlier function installed by this + mapping _might_, depending on how it's bound, be uninstalled. + If v is not one of those types, it must be a Function, for + which this method creates (if needed) a WASM function binding + and returns the WASM pointer to that binding. + + If this instance is not in 'transient' mode, it will remember + the binding for at least the next call, to avoid recreating the + function binding unnecessarily. + + If it's passed a pointer(ish) value for v, it assumes it's a + WASM function pointer and does _not_ perform any function + binding, so this object's bindMode is irrelevant/ignored for + such cases. + + See the parent class's convertArg() docs for details on what + exactly the 2nd and 3rd arguments are. + */ + convertArg(v, argv, argIndex) { + let pair = this.singleton; + if (!pair && this.isContext) pair = this.contextMap(this.contextKey(argv, argIndex)); + if (pair && 2 === pair.length && pair[0] === v) return pair[1]; + if (v instanceof Function) { + if (this.callProxy) v = this.callProxy(v); + const fp = __installFunction(v, this.signature, this.isTransient); + if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this, this.contextKey(argv, argIndex), "@" + fp, v); + if (pair) { + if (pair[1]) { + if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); + try { + cache.scopedAlloc.pushPtr(pair[1]); + } catch (e) {} + } + pair[0] = arguments[0] || __NullPtr; + pair[1] = fp; + } + return fp; + } else if (target.isPtr(v) || null === v || void 0 === v) { + if (pair && pair[1] && pair[1] !== v) { + if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); + try { + cache.scopedAlloc.pushPtr(pair[1]); + } catch (e) {} + pair[0] = pair[1] = v || __NullPtr; + } + return v || __NullPtr; + } else throw new TypeError("Invalid FuncPtrAdapter argument type. Expecting a function pointer or a " + (this.name ? this.name + " " : "") + "function matching signature " + this.signature + "."); + } + }; + /** If true, the constructor emits a warning. The intent is that + this be set to true after bootstrapping of the higher-level + client library is complete, to warn downstream clients that + they shouldn't be relying on this implementation detail which + does not have a stable interface. */ + xArg.FuncPtrAdapter.warnOnUse = false; + /** If true, convertArg() will call FuncPtrAdapter.debugOut() when + it (un)installs a function binding to/from WASM. Note that + deinstallation of bindScope=transient bindings happens via + scopedAllocPop() so will not be output. */ + xArg.FuncPtrAdapter.debugFuncInstall = false; + /** Function used for debug output. */ + xArg.FuncPtrAdapter.debugOut = console.debug.bind(console); + /** + List of legal values for the FuncPtrAdapter bindScope config + option. + */ + xArg.FuncPtrAdapter.bindScopes = [ + "transient", + "context", + "singleton", + "permanent" + ]; + /** Throws if xArg.get(t) returns falsy. */ + const __xArgAdapterCheck = (t) => xArg.get(t) || toss("Argument adapter not found:", t); + /** Throws if xResult.get(t) returns falsy. */ + const __xResultAdapterCheck = (t) => xResult.get(t) || toss("Result adapter not found:", t); + /** + Fetches the xWrap() argument adapter mapped to t, calls it, + passing in all remaining arguments, and returns the result. + Throws if t is not mapped to an argument converter. + */ + cache.xWrap.convertArg = (t, ...args) => __xArgAdapterCheck(t)(...args); + /** + Identical to convertArg() except that it does not perform + an is-defined check on the mapping to t before invoking it. + */ + cache.xWrap.convertArgNoCheck = (t, ...args) => xArg.get(t)(...args); + /** + Fetches the xWrap() result adapter mapped to t, calls it, passing + it v, and returns the result. Throws if t is not mapped to an + argument converter. + */ + cache.xWrap.convertResult = (t, v) => null === t ? v : t ? __xResultAdapterCheck(t)(v) : void 0; + /** + Identical to convertResult() except that it does not perform an + is-defined check on the mapping to t before invoking it. + */ + cache.xWrap.convertResultNoCheck = (t, v) => null === t ? v : t ? xResult.get(t)(v) : void 0; + /** + Creates a wrapper for another function which converts the arguments + of the wrapper to argument types accepted by the wrapped function, + then converts the wrapped function's result to another form + for the wrapper. + + The first argument must be one of: + + - A JavaScript function. + - The name of a WASM-exported function. xGet() is used to fetch + the exported function, which throws if it's not found. + - A pointer into the indirect function table. e.g. a pointer + returned from target.installFunction(). + + It returns either the passed-in function or a wrapper for that + function which converts the JS-side argument types into WASM-side + types and converts the result type. + + The second argument, `resultType`, describes the conversion for + the wrapped functions result. A literal `null` or the string + `'null'` both mean to return the original function's value as-is + (mnemonic: there is "null" conversion going on). Literal + `undefined` or the string `"void"` both mean to ignore the + function's result and return `undefined`. Aside from those two + special cases, the `resultType` value may be one of the values + described below or any mapping installed by the client using + xWrap.resultAdapter(). + + If passed 3 arguments and the final one is an array, that array + must contain a list of type names (see below) for adapting the + arguments from JS to WASM. If passed 2 arguments, more than 3, + or the 3rd is not an array, all arguments after the 2nd (if any) + are treated as type names. i.e.: + + ``` + xWrap('funcname', 'i32', 'string', 'f64'); + // is equivalent to: + xWrap('funcname', 'i32', ['string', 'f64']); + ``` + + This function enforces that the given list of arguments has the + same arity as the being-wrapped function (as defined by its + `length` property) and it will throw if that is not the case. + Similarly, the created wrapper will throw if passed a differing + argument count. The intent of that strictness is to help catch + coding errors in using JS-bound WASM functions earlier rather + than laer. + + Type names are symbolic names which map the arguments to an + adapter function to convert, if needed, the value before passing + it on to WASM or to convert a return result from WASM. The list + of pre-defined names: + + - `i8`, `i16`, `i32` (args and results): all integer conversions + which convert their argument to an integer and truncate it to + the given bit length. + + - `*`, `**`, and `pointer` (args): are assumed to be WASM pointer + values and are returned coerced to an appropriately-sized + pointer value (i32 or i64). Non-numeric values will coerce to 0 + and out-of-range values will have undefined results (just as + with any pointer misuse). + + - `*` and `pointer` (results): aliases for the current + WASM pointer numeric type. + + - `**` (args): is simply a descriptive alias for the WASM pointer + type. It's primarily intended to mark output-pointer arguments, + noting that JS's view of WASM does not distinguish between + pointers and pointers-to-pointers, so all such interpretation + of `**`, as distinct from `*`, necessarily happens at the + client level. + + - `NumType*` (args): a type name in this form, where T is + the name of a numeric mapping, e.g. 'int16' or 'double', + is treated like `*`. + + - `i64` (args and results): passes the value to BigInt() to + convert it to an int64. This conversion will if bigIntEnabled + is falsy. + + - `f32` (`float`), `f64` (`double`) (args and results): pass + their argument to Number(). i.e. the adapter does not currently + distinguish between the two types of floating-point numbers. + + - `number` (results): converts the result to a JS Number using + Number(theValue). This is for result conversions only, as it's + not possible to generically know which type of number to + convert arguments to. + + Non-numeric conversions include: + + - `null` literal or `"null"` string (args and results): perform + no translation and pass the arg on as-is. This is primarily + useful for results but may have a use or two for arguments. + + - `string` or `utf8` (args): has two different semantics in order + to accommodate various uses of certain C APIs + (e.g. output-style strings)... + + - If the arg is a JS string, it creates a _temporary_ + UTF-8-encoded C-string to pass to the exported function, + cleaning it up before the wrapper returns. If a long-lived + C-string pointer is required, that requires client-side code + to create the string then pass its pointer to the function. + + - Else the arg is assumed to be a pointer to a string the + client has already allocated and it's passed on as + a WASM pointer. + + - `string` or `utf8` (results): treats the result value as a + const C-string, encoded as UTF-8, copies it to a JS string, + and returns that JS string. + + - `string:dealloc` or `utf8:dealloc` (results): treats the result + value as a non-const UTF-8 C-string, ownership of which has + just been transfered to the caller. It copies the C-string to a + JS string, frees the C-string using dealloc(), and returns the + JS string. If such a result value is NULL, the JS result is + `null`. Achtung: when using an API which returns results from a + specific allocator, e.g. `my_malloc()`, this conversion _is not + legal_. Instead, an equivalent conversion which uses the + appropriate deallocator is required. For example: + + ```js + target.xWrap.resultAdapter('string:my_free',(i)=>{ + try { return i ? target.cstrToJs(i) : null; } + finally{ target.exports.my_free(i); } + }; + ``` + + - `json` (results): treats the result as a const C-string and + returns the result of passing the converted-to-JS string to + JSON.parse(). Returns `null` if the C-string is a NULL pointer. + + - `json:dealloc` (results): works exactly like `string:dealloc` but + returns the same thing as the `json` adapter. Note the + warning in `string:dealloc` regarding matching allocators and + deallocators. + + The type names for results and arguments are validated when + xWrap() is called and any unknown names will trigger an + exception. + + Clients may map their own result and argument adapters using + xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all + type conversions are valid for both arguments _and_ result types + as they often have different memory ownership requirements. + + Design note: the ability to pass in a JS function as the first + argument is of relatively limited use, primarily for testing + argument and result converters. JS functions, by and large, will + not want to deal with C-type arguments. + + TODOs: + + - Figure out how/whether we can (semi-)transparently handle + pointer-type _output_ arguments. Those currently require + explicit handling by allocating pointers, assigning them before + the call using poke(), and fetching them with + peek() after the call. We may be able to automate some + or all of that. + + - Figure out whether it makes sense to extend the arg adapter + interface such that each arg adapter gets an array containing + the results of the previous arguments in the current call. That + might allow some interesting type-conversion feature. Use case: + handling of the final argument to sqlite3_prepare_v2() depends + on the type (pointer vs JS string) of its 2nd + argument. Currently that distinction requires hand-writing a + wrapper for that function. That case is unusual enough that + abstracting it into this API (and taking on the associated + costs) may well not make good sense. + */ + target.xWrap = function callee(fArg, resultType, ...argTypes) { + if (3 === arguments.length && Array.isArray(arguments[2])) argTypes = arguments[2]; + if (target.isPtr(fArg)) fArg = target.functionEntry(fArg) || toss("Function pointer not found in WASM function table."); + const fIsFunc = fArg instanceof Function; + const xf = fIsFunc ? fArg : target.xGet(fArg); + if (fIsFunc) fArg = xf.name || "unnamed function"; + if (argTypes.length !== xf.length) __argcMismatch(fArg, xf.length); + if (0 === xf.length && (null === resultType || "null" === resultType)) return xf; + __xResultAdapterCheck(resultType); + for (const t of argTypes) if (t instanceof AbstractArgAdapter) xArg.set(t, (...args) => t.convertArg(...args)); + else __xArgAdapterCheck(t); + const cxw = cache.xWrap; + if (0 === xf.length) return (...args) => args.length ? __argcMismatch(fArg, xf.length) : cxw.convertResult(resultType, xf.call(null)); + return function(...args) { + if (args.length !== xf.length) __argcMismatch(fArg, xf.length); + const scope = target.scopedAllocPush(); + try { + let i = 0; + if (callee.debug) console.debug("xWrap() preparing: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); + for (; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], args, i); + if (callee.debug) console.debug("xWrap() calling: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); + return cxw.convertResultNoCheck(resultType, xf.apply(null, args)); + } finally { + target.scopedAllocPop(scope); + } + }; + }; + /** + Internal impl for xWrap.resultAdapter() and argAdapter(). + + func = one of xWrap.resultAdapter or xWrap.argAdapter. + + argc = the number of args in the wrapping call to this + function. + + typeName = the first arg to the wrapping function. + + adapter = the second arg to the wrapping function. + + modeName = a descriptive name of the wrapping function for + error-reporting purposes. + + xcvPart = one of xResult or xArg. + + This acts as either a getter (if 1===argc) or setter (if + 2===argc) for the given adapter. Returns func on success or + throws if (A) called with 2 args but adapter is-not-a Function or + (B) typeName is not a string or (C) argc is not one of (1, 2). + */ + const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart) { + if ("string" === typeof typeName) { + if (1 === argc) return xcvPart.get(typeName); + else if (2 === argc) { + if (!adapter) { + xcvPart.delete(typeName); + return func; + } else if (!(adapter instanceof Function)) toss(modeName, "requires a function argument."); + xcvPart.set(typeName, adapter); + return func; + } + } + toss("Invalid arguments to", modeName); + }; + /** + Gets, sets, or removes a result value adapter for use with + xWrap(). If passed only 1 argument, the adapter function for the + given type name is returned. If the second argument is explicit + falsy (as opposed to defaulted), the adapter named by the first + argument is removed. If the 2nd argument is not falsy, it must be + a function which takes one value and returns a value appropriate + for the given type name. The adapter may throw if its argument is + not of a type it can work with. This function throws for invalid + arguments. + + Example: + + ``` + xWrap.resultAdapter('twice',(v)=>v+v); + ``` + + Result adapters MUST NOT use the scopedAlloc() family of APIs to + allocate a result value. xWrap()-generated wrappers run in the + context of scopedAllocPush() so that argument adapters can easily + convert, e.g., to C-strings, and have them cleaned up + automatically before the wrapper returns to the caller. Likewise, + if a _result_ adapter uses scoped allocation, the result will be + freed before the wrapper returns, leading to chaos and undefined + behavior. + + When called as a setter, this function returns itself. + */ + target.xWrap.resultAdapter = function f(typeName, adapter) { + return __xAdapter(f, arguments.length, typeName, adapter, "resultAdapter()", xResult); + }; + /** + Functions identically to xWrap.resultAdapter() but applies to + call argument conversions instead of result value conversions. + + xWrap()-generated wrappers perform argument conversion in the + context of a scopedAllocPush(), so any memory allocation + performed by argument adapters really, really, really should be + made using the scopedAlloc() family of functions unless + specifically necessary. For example: + + ``` + xWrap.argAdapter('my-string', function(v){ + return ('string'===typeof v) + ? myWasmObj.scopedAllocCString(v) : null; + }; + ``` + + Contrariwise, _result_ adapters _must not_ use scopedAlloc() to + allocate results because they would be freed before the + xWrap()-created wrapper returns. + + It is perfectly legitimate to use these adapters to perform + argument validation, as opposed (or in addition) to conversion. + When used that way, they should throw for invalid arguments. + */ + target.xWrap.argAdapter = function f(typeName, adapter) { + return __xAdapter(f, arguments.length, typeName, adapter, "argAdapter()", xArg); + }; + target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter; + /** + Functions like xCall() but performs argument and result type + conversions as for xWrap(). The first, second, and third + arguments are as documented for xWrap(), except that the 3rd + argument may be either a falsy value or empty array to represent + nullary functions. The 4th+ arguments are arguments for the call, + with the special case that if the 4th argument is an array, it is + used as the arguments for the call. Returns the converted result + of the call. + + This is just a thin wrapper around xWrap(). If the given function + is to be called more than once, it's more efficient to use + xWrap() to create a wrapper, then to call that wrapper as many + times as needed. For one-shot calls, however, this variant is + simpler. + */ + target.xCallWrapped = function(fArg, resultType, argTypes, ...args) { + if (Array.isArray(arguments[3])) args = arguments[3]; + return target.xWrap(fArg, resultType, argTypes || []).apply(null, args || []); + }; + /** + This function is ONLY exposed in the public API to facilitate + testing. It should not be used in application-level code, only + in test code. + + Expects to be given (typeName, value) and returns a conversion + of that value as has been registered using argAdapter(). + It throws if no adapter is found. + + ACHTUNG: the adapter may require that a scopedAllocPush() is + active and it may allocate memory within that scope. It may also + require additional arguments, depending on the type of + conversion. + */ + target.xWrap.testConvertArg = cache.xWrap.convertArg; + /** + This function is ONLY exposed in the public API to facilitate + testing. It should not be used in application-level code, only + in test code. + + Expects to be given (typeName, value) and returns a conversion + of that value as has been registered using resultAdapter(). + It throws if no adapter is found. + + ACHTUNG: the adapter may allocate memory which the caller may need + to know how to free. + */ + target.xWrap.testConvertResult = cache.xWrap.convertResult; + return target; + }; + /** + yawl (Yet Another Wasm Loader) provides very basic wasm loader. + It requires a config object: + + - `uri`: required URI of the WASM file to load. + + - `onload(loadResult)`: optional callback. Its argument is an + object described in more detail below. + + - `imports`: optional imports object for + WebAssembly.instantiate[Streaming](). The default is an empty + set of imports. If the module requires any imports, this object + must include them. + + - `wasmUtilTarget`: optional object suitable for passing to + WhWasmUtilInstaller(). If set, it gets passed to that function + before the returned promise resolves. This function sets several + properties on it before passing it on to that function (which + sets many more): + + - `module`, `instance`: the properties from the + instantiate[Streaming]() result. + + - If `instance.exports.memory` is _not_ set then it requires that + `config.imports.env.memory` be set (else it throws), and + assigns that to `wasmUtilTarget.memory`. + + - If `wasmUtilTarget.alloc` is not set and + `instance.exports.malloc` is, it installs + `wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()` + wrappers for the exports' `malloc` and `free` functions + if exports.malloc exists. + + It returns a function which, when called, initiates loading of the + module and returns a Promise. When that Promise resolves, it calls + the `config.onload` callback (if set) and passes it `(loadResult)`, + where `loadResult` is derived from the result of + WebAssembly.instantiate[Streaming](), an object in the form: + + ``` + { + module: a WebAssembly.Module, + instance: a WebAssembly.Instance, + config: the config arg to this function + } + ``` + + (The initial `then()` attached to the promise gets only that + object, and not the `config` object, thus the potential need for a + `config.onload` handler.) + + Error handling is up to the caller, who may attach a `catch()` call + to the promise. + */ + globalThis.WhWasmUtilInstaller.yawl = function yawl(config) { + "use strict"; + const wfetch = () => fetch(config.uri, { credentials: "same-origin" }); + const wui = this; + const finalThen = function(arg) { + if (config.wasmUtilTarget) { + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + const tgt = config.wasmUtilTarget; + tgt.module = arg.module; + tgt.instance = arg.instance; + if (!tgt.instance.exports.memory) + /** + WhWasmUtilInstaller requires either tgt.exports.memory + (exported from WASM) or tgt.memory (JS-provided memory + imported into WASM). + */ + tgt.memory = config?.imports?.env?.memory || toss("Missing 'memory' object!"); + if (!tgt.alloc && arg.instance.exports.malloc) { + const exports = arg.instance.exports; + tgt.alloc = function(n) { + return exports.malloc(n) || toss("Allocation of", n, "bytes failed."); + }; + tgt.dealloc = function(m) { + m && exports.free(m); + }; + } + wui(tgt); + } + arg.config = config; + if (config.onload) config.onload(arg); + return arg; + }; + return WebAssembly.instantiateStreaming ? () => WebAssembly.instantiateStreaming(wfetch(), config.imports || {}).then(finalThen) : () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, config.imports || {})).then(finalThen); + }.bind(globalThis.WhWasmUtilInstaller); + globalThis.Jaccwabyt = function StructBinderFactory(config) { + "use strict"; + /** Throws a new Error, the message of which is the concatenation + all args with a space between each. */ + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + { + let h = config.heap; + if (h instanceof WebAssembly.Memory) h = function() { + return new Uint8Array(this.buffer); + }.bind(h); + else if (!(h instanceof Function)) toss("config.heap must be WebAssembly.Memory instance or", "a function which returns one."); + config.heap = h; + } + ["alloc", "dealloc"].forEach(function(k) { + config[k] instanceof Function || toss("Config option '" + k + "' must be a function."); + }); + const SBF = StructBinderFactory, heap = config.heap, alloc = config.alloc, dealloc = config.dealloc; + config.realloc; + const log = config.log || console.debug.bind(console), memberPrefix = config.memberPrefix || "", memberSuffix = config.memberSuffix || "", BigInt = globalThis["BigInt"], BigInt64Array = globalThis["BigInt64Array"], bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array; + let ptr; + const ptrSize = config.pointerSize || config.ptrSize || ("bigint" === typeof (ptr = alloc(1)) ? 8 : 4); + const ptrIR = config.pointerIR || config.ptrIR || (4 === ptrSize ? "i32" : "i64"); + if (ptr) { + dealloc(ptr); + ptr = void 0; + } + if (ptrSize !== 4 && ptrSize !== 8) toss("Invalid pointer size:", ptrSize); + if (ptrIR !== "i32" && ptrIR !== "i64") toss("Invalid pointer representation:", ptrIR); + /** Either BigInt or, if !bigIntEnabled, a function which + throws complaining that BigInt is not enabled. */ + const __BigInt = bigIntEnabled && BigInt ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); + const __asPtrType = "i32" == ptrIR ? Number : __BigInt; + const __NullPtr = __asPtrType(0); + /** + Expects any number of numeric arguments, each one of either type + Number or BigInt. It sums them up (from an implicit starting + point of 0 or 0n) and returns them as a number of the same type + which target.asPtrType() uses. + + This is a workaround for not being able to mix Number/BigInt in + addition/subtraction expressions (which we frequently need for + calculating pointer offsets). + */ + const __ptrAdd = function(...args) { + let rc = __NullPtr; + for (let i = 0; i < args.length; ++i) rc += __asPtrType(args[i]); + return rc; + }; + const __ptrAddSelf = function(...args) { + return __ptrAdd(this.pointer, ...args); + }; + if (!SBF.debugFlags) { + SBF.__makeDebugFlags = function(deriveFrom = null) { + if (deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags; + const f = function f(flags) { + if (0 === arguments.length) return f.__flags; + if (flags < 0) { + delete f.__flags.getter; + delete f.__flags.setter; + delete f.__flags.alloc; + delete f.__flags.dealloc; + } else { + f.__flags.getter = 0 !== (1 & flags); + f.__flags.setter = 0 !== (2 & flags); + f.__flags.alloc = 0 !== (4 & flags); + f.__flags.dealloc = 0 !== (8 & flags); + } + return f._flags; + }; + Object.defineProperty(f, "__flags", { + iterable: false, + writable: false, + value: Object.create(deriveFrom) + }); + if (!deriveFrom) f(0); + return f; + }; + SBF.debugFlags = SBF.__makeDebugFlags(); + } + const isLittleEndian = true; + /** + Some terms used in the internal docs: + + StructType: a struct-wrapping class generated by this + framework. + + DEF: struct description object. + + SIG: struct member signature string. + */ + /** True if SIG s looks like a function signature, else + false. */ + const isFuncSig = (s) => "(" === s[1]; + /** True if SIG s is-a pointer-type signature. */ + const isPtrSig = (s) => "p" === s || "P" === s || "s" === s; + const isAutoPtrSig = (s) => "P" === s; + /** Returns p if SIG s is a function SIG, else returns s[0]. */ + const sigLetter = (s) => s ? isFuncSig(s) ? "p" : s[0] : void 0; + /** Returns the WASM IR form of the letter at SIG s[0]. Throws for + an unknown SIG. */ + const sigIR = function(s) { + switch (sigLetter(s)) { + case "c": + case "C": return "i8"; + case "i": return "i32"; + case "p": + case "P": + case "s": return ptrIR; + case "j": return "i64"; + case "f": return "float"; + case "d": return "double"; + } + toss("Unhandled signature IR:", s); + }; + /** Returns the WASM sizeof of the letter at SIG s[0]. Throws for an + unknown SIG. */ + const sigSize = function(s) { + switch (sigLetter(s)) { + case "c": + case "C": return 1; + case "i": return 4; + case "p": + case "P": + case "s": return ptrSize; + case "j": return 8; + case "f": return 4; + case "d": return 8; + } + toss("Unhandled signature sizeof:", s); + }; + const affirmBigIntArray = BigInt64Array ? () => true : () => toss("BigInt64Array is not available."); + /** Returns the name of a DataView getter method corresponding + to the given SIG. */ + const sigDVGetter = function(s) { + switch (sigLetter(s)) { + case "p": + case "P": + case "s": + switch (ptrSize) { + case 4: return "getInt32"; + case 8: return affirmBigIntArray() && "getBigInt64"; + } + break; + case "i": return "getInt32"; + case "c": return "getInt8"; + case "C": return "getUint8"; + case "j": return affirmBigIntArray() && "getBigInt64"; + case "f": return "getFloat32"; + case "d": return "getFloat64"; + } + toss("Unhandled DataView getter for signature:", s); + }; + /** Returns the name of a DataView setter method corresponding + to the given SIG. */ + const sigDVSetter = function(s) { + switch (sigLetter(s)) { + case "p": + case "P": + case "s": + switch (ptrSize) { + case 4: return "setInt32"; + case 8: return affirmBigIntArray() && "setBigInt64"; + } + break; + case "i": return "setInt32"; + case "c": return "setInt8"; + case "C": return "setUint8"; + case "j": return affirmBigIntArray() && "setBigInt64"; + case "f": return "setFloat32"; + case "d": return "setFloat64"; + } + toss("Unhandled DataView setter for signature:", s); + }; + /** + Returns a factory for either Number or BigInt, depending on the + given SIG. This constructor is used in property setters to coerce + the being-set value to the correct pointer size. + */ + const sigDVSetWrapper = function(s) { + switch (sigLetter(s)) { + case "i": + case "f": + case "c": + case "C": + case "d": return Number; + case "j": return __BigInt; + case "p": + case "P": + case "s": + switch (ptrSize) { + case 4: return Number; + case 8: return __BigInt; + } + break; + } + toss("Unhandled DataView set wrapper for signature:", s); + }; + /** Returns the given struct and member name in a form suitable for + debugging and error output. */ + const sPropName = (s, k) => s + "::" + k; + const __propThrowOnSet = function(structName, propName) { + return () => toss(sPropName(structName, propName), "is read-only."); + }; + /** + In order to completely hide StructBinder-bound struct pointers + from JS code, we store them in a scope-local WeakMap which maps + the struct-bound objects to an object with their metadata: + + { + .p = the native pointer, + .o = self (for an eventual reverse-mapping), + .xb = extra bytes allocated for p, + .zod = zeroOnDispose, + .ownsPointer = true if this object owns p + } + + The .p data are accessible via obj.pointer, which is gated behind + a property interceptor, but are not exposed anywhere else in the + public API. + */ + const getInstanceHandle = function f(obj, create = true) { + let ii = f.map.get(obj); + if (!ii && create) f.map.set(obj, ii = f.create(obj)); + return ii; + }; + getInstanceHandle.map = /* @__PURE__ */ new WeakMap(); + getInstanceHandle.create = (forObj) => { + return Object.assign(Object.create(null), { + o: forObj, + p: void 0, + ownsPointer: false, + zod: false, + xb: 0 + }); + }; + /** + Remove the getInstanceHandle() mapping for obj. + */ + const rmInstanceHandle = (obj) => getInstanceHandle.map.delete(obj); + const __isPtr32 = (ptr) => "number" === typeof ptr && ptr === (ptr | 0) && ptr >= 0; + const __isPtr64 = (ptr) => "bigint" === typeof ptr && ptr >= 0 || __isPtr32(ptr); + /** + isPtr() is an alias for isPtr32() or isPtr64(), depending on + ptrSize. + */ + const __isPtr = 4 === ptrSize ? __isPtr32 : __isPtr64; + const __isNonNullPtr = (v) => __isPtr(v) && v > 0; + /** Frees the obj.pointer memory (a.k.a. m), handles obj.ondispose, + and unmaps obj from its native resources. */ + const __freeStruct = function(ctor, obj, m) { + const ii = getInstanceHandle(obj, false); + if (!ii) return; + rmInstanceHandle(obj); + if (!m && !(m = ii.p)) { + console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo"); + return; + } + if (Array.isArray(obj.ondispose)) { + let x; + while (x = obj.ondispose.pop()) try { + if (x instanceof Function) x.call(obj); + else if (x instanceof StructType) x.dispose(); + else if (__isPtr(x)) dealloc(x); + } catch (e) { + console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); + } + } else if (obj.ondispose instanceof Function) try { + obj.ondispose(); + } catch (e) { + console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); + } + delete obj.ondispose; + if (ctor.debugFlags.__flags.dealloc) log("debug.dealloc:", ii.ownsPointer ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + m); + if (ii.ownsPointer) { + if (ii.zod || ctor.structInfo.zeroOnDispose) heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof + ii.xb); + dealloc(m); + } + }; + /** Returns a skeleton for a read-only, non-iterable property + * descriptor. */ + const rop0 = () => { + return { + configurable: false, + writable: false, + iterable: false + }; + }; + /** Returns a skeleton for a read-only property accessor wrapping + value v. */ + const rop = (v) => { + return { + ...rop0(), + value: v + }; + }; + /** Allocates obj's memory buffer based on the size defined in + ctor.structInfo.sizeof. */ + const __allocStruct = function f(ctor, obj, xm) { + let opt; + const checkPtr = (ptr) => { + __isNonNullPtr(ptr) || toss("Invalid pointer value", arguments[0], "for", ctor.structName, "constructor."); + }; + if (arguments.length >= 3) if (xm && "object" === typeof xm) { + opt = xm; + xm = opt?.wrap; + } else { + checkPtr(xm); + opt = { wrap: xm }; + } + else opt = {}; + const fill = !xm; + let nAlloc = 0; + let ownsPointer = false; + if (xm) { + checkPtr(xm); + ownsPointer = !!opt?.takeOwnership; + } else { + const nX = opt?.extraBytes ?? 0; + if (nX < 0 || nX !== (nX | 0)) toss("Invalid extraBytes value:", opt?.extraBytes); + nAlloc = ctor.structInfo.sizeof + nX; + xm = alloc(nAlloc) || toss("Allocation of", ctor.structName, "structure failed."); + ownsPointer = true; + } + try { + if (opt?.debugFlags) obj.debugFlags(opt.debugFlags); + if (ctor.debugFlags.__flags.alloc) log("debug.alloc:", fill ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + xm); + if (fill) heap().fill(0, Number(xm), Number(xm) + nAlloc); + const ii = getInstanceHandle(obj); + ii.p = xm; + ii.ownsPointer = ownsPointer; + ii.xb = nAlloc ? nAlloc - ctor.structInfo.sizeof : 0; + ii.zod = !!opt?.zeroOnDispose; + if (opt?.ondispose && opt.ondispose !== xm) obj.addOnDispose(opt.ondispose); + } catch (e) { + __freeStruct(ctor, obj, xm); + throw e; + } + }; + /** True if sig looks like an emscripten/jaccwabyt + type signature, else false. */ + const looksLikeASig = function f(sig) { + f.rxSig1 ??= /^[ipPsjfdcC]$/; + f.rxSig2 ??= /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/; + return f.rxSig1.test(sig) || f.rxSig2.test(sig); + }; + /** Returns a pair of adaptor maps (objects) in a length-3 + array specific to the given object. */ + const __adaptorsFor = function(who) { + let x = this.get(who); + if (!x) { + x = [ + Object.create(null), + Object.create(null), + Object.create(null) + ]; + this.set(who, x); + } + return x; + }.bind(/* @__PURE__ */ new WeakMap()); + /** Code de-duplifier for __adaptGet(), __adaptSet(), and + __adaptStruct(). */ + const __adaptor = function(who, which, key, proxy) { + const a = __adaptorsFor(who)[which]; + if (3 === arguments.length) return a[key]; + if (proxy) return a[key] = proxy; + return delete a[key]; + }; + const __adaptGet = function(key, ...args) { + return __adaptor(this, 0, key, ...args); + }; + const __affirmNotASig = function(ctx, key) { + looksLikeASig(key) && toss(ctx, "(", key, ") collides with a data type signature."); + }; + const __adaptSet = function(key, ...args) { + __affirmNotASig("Setter adaptor", key); + return __adaptor(this, 1, key, ...args); + }; + const __adaptStruct = function(key, ...args) { + __affirmNotASig("Struct adaptor", key); + return __adaptor(this, 2, key, ...args); + }; + /** + An internal counterpart of __adaptStruct(). If key is-a string, + uses __adaptor(who) to fetch the struct-adaptor entry for key, + else key is assumed to be a struct description object. If it + resolves to an object, that's returned, else an exception is + thrown. + */ + const __adaptStruct2 = function(who, key) { + const si = "string" === typeof key ? __adaptor(who, 2, key) : key; + if ("object" !== typeof si) toss("Invalid struct mapping object. Arg =", key, JSON.stringify(si)); + return si; + }; + const __memberKey = (k) => memberPrefix + k + memberSuffix; + const __memberKeyProp = rop(__memberKey); + /** + Looks up a struct member in structInfo.members. Throws if found + if tossIfNotFound is true, else returns undefined if not + found. The given name may be either the name of the + structInfo.members key (faster) or the key as modified by the + memberPrefix and memberSuffix settings. + */ + const __lookupMember = function(structInfo, memberName, tossIfNotFound = true) { + let m = structInfo.members[memberName]; + if (!m && (memberPrefix || memberSuffix)) { + for (const v of Object.values(structInfo.members)) if (v.key === memberName) { + m = v; + break; + } + if (!m && tossIfNotFound) toss(sPropName(structInfo.name || structInfo.structName, memberName), "is not a mapped struct member."); + } + return m; + }; + /** + Uses __lookupMember(obj.structInfo,memberName) to find a member, + throwing if not found. Returns its signature, either in this + framework's native format or in Emscripten format. + */ + const __memberSignature = function f(obj, memberName, emscriptenFormat = false) { + if (!f._) f._ = (x) => x.replace(/[^vipPsjrdcC]/g, "").replace(/[pPscC]/g, "i"); + const m = __lookupMember(obj.structInfo, memberName, true); + return emscriptenFormat ? f._(m.signature) : m.signature; + }; + /** Impl of X.memberKeys() for StructType and struct ctors. */ + const __structMemberKeys = rop(function() { + const a = []; + for (const k of Object.keys(this.structInfo.members)) a.push(this.memberKey(k)); + return a; + }); + const __utf8Decoder = new TextDecoder("utf-8"); + const __utf8Encoder = new TextEncoder(); + /** Internal helper to use in operations which need to distinguish + between SharedArrayBuffer heap memory and non-shared heap. */ + const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; + const __utf8Decode = function(arrayBuffer, begin, end) { + if (8 === ptrSize) { + begin = Number(begin); + end = Number(end); + } + return __utf8Decoder.decode(arrayBuffer.buffer instanceof __SAB ? arrayBuffer.slice(begin, end) : arrayBuffer.subarray(begin, end)); + }; + /** + Uses __lookupMember() to find the given obj.structInfo key. + Returns that member if it is a string, else returns false. If the + member is not found, throws if tossIfNotFound is true, else + returns false. + */ + const __memberIsString = function(obj, memberName, tossIfNotFound = false) { + const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound); + return m && 1 === m.signature.length && "s" === m.signature[0] ? m : false; + }; + /** + Given a member description object, throws if member.signature is + not valid for assigning to or interpretation as a C-style string. + It optimistically assumes that any signature of (i,p,s) is + C-string compatible. + */ + const __affirmCStringSignature = function(member) { + if ("s" === member.signature) return; + toss("Invalid member type signature for C-string value:", JSON.stringify(member)); + }; + /** + Looks up the given member in obj.structInfo. If it has a + signature of 's' then it is assumed to be a C-style UTF-8 string + and a decoded copy of the string at its address is returned. If + the signature is of any other type, it throws. If an s-type + member's address is 0, `null` is returned. + */ + const __memberToJsString = function f(obj, memberName) { + const m = __lookupMember(obj.structInfo, memberName, true); + __affirmCStringSignature(m); + const addr = obj[m.key]; + if (!addr) return null; + let pos = addr; + const mem = heap(); + for (; mem[pos] !== 0; ++pos); + return addr === pos ? "" : __utf8Decode(mem, addr, pos); + }; + /** + Adds value v to obj.ondispose, creating ondispose, + or converting it to an array, if needed. + */ + const __addOnDispose = function(obj, ...v) { + if (obj.ondispose) { + if (!Array.isArray(obj.ondispose)) obj.ondispose = [obj.ondispose]; + } else obj.ondispose = []; + obj.ondispose.push(...v); + }; + /** + Allocates a new UTF-8-encoded, NUL-terminated copy of the given + JS string and returns its address relative to heap(). If + allocation returns 0 this function throws. Ownership of the + memory is transfered to the caller, who must eventually pass it + to the configured dealloc() function. + */ + const __allocCString = function(str) { + const u = __utf8Encoder.encode(str); + const mem = alloc(u.length + 1); + if (!mem) toss("Allocation error while duplicating string:", str); + const h = heap(); + h.set(u, Number(mem)); + h[__ptrAdd(mem, u.length)] = 0; + return mem; + }; + /** + Sets the given struct member of obj to a dynamically-allocated, + UTF-8-encoded, NUL-terminated copy of str. It is up to the caller + to free any prior memory, if appropriate. The newly-allocated + string is added to obj.ondispose so will be freed when the object + is disposed. + + The given name may be either the name of the structInfo.members + key (faster) or the key as modified by the memberPrefix and + memberSuffix settings. + */ + const __setMemberCString = function(obj, memberName, str) { + const m = __lookupMember(obj.structInfo, memberName, true); + __affirmCStringSignature(m); + const mem = __allocCString(str); + obj[m.key] = mem; + __addOnDispose(obj, mem); + return obj; + }; + /** + Prototype for all StructFactory instances (the constructors + returned from StructBinder). + */ + const StructType = function StructType(structName, structInfo) { + if (arguments[2] !== rop) toss("Do not call the StructType constructor", "from client-level code."); + Object.defineProperties(this, { + structName: rop(structName), + structInfo: rop(structInfo) + }); + }; + /** + Properties inherited by struct-type-specific StructType instances + and (indirectly) concrete struct-type instances. + */ + StructType.prototype = Object.create(null, { + dispose: rop(function() { + __freeStruct(this.constructor, this); + }), + lookupMember: rop(function(memberName, tossIfNotFound = true) { + return __lookupMember(this.structInfo, memberName, tossIfNotFound); + }), + memberToJsString: rop(function(memberName) { + return __memberToJsString(this, memberName); + }), + memberIsString: rop(function(memberName, tossIfNotFound = true) { + return __memberIsString(this, memberName, tossIfNotFound); + }), + memberKey: __memberKeyProp, + memberKeys: __structMemberKeys, + memberSignature: rop(function(memberName, emscriptenFormat = false) { + return __memberSignature(this, memberName, emscriptenFormat); + }), + memoryDump: rop(function() { + const p = this.pointer; + return p ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof)) : null; + }), + extraBytes: { + configurable: false, + enumerable: false, + get: function() { + return getInstanceHandle(this, false)?.xb ?? 0; + } + }, + zeroOnDispose: { + configurable: false, + enumerable: false, + get: function() { + return getInstanceHandle(this, false)?.zod ?? !!this.structInfo.zeroOnDispose; + } + }, + pointer: { + configurable: false, + enumerable: false, + get: function() { + return getInstanceHandle(this, false)?.p; + }, + set: () => toss("Cannot assign the 'pointer' property of a struct.") + }, + setMemberCString: rop(function(memberName, str) { + return __setMemberCString(this, memberName, str); + }) + }); + Object.assign(StructType.prototype, { addOnDispose: function(...v) { + __addOnDispose(this, ...v); + return this; + } }); + /** + "Static" properties for StructType. + */ + Object.defineProperties(StructType, { + allocCString: rop(__allocCString), + isA: rop((v) => v instanceof StructType), + hasExternalPointer: rop((v) => { + const ii = getInstanceHandle(v, false); + return !!(ii?.p && !ii?.ownsPointer); + }), + memberKey: __memberKeyProp + }); + /** + If struct description object si has a getter proxy, return it (a + function), else return undefined. + */ + const memberGetterProxy = function(si) { + return si.get || (si.adaptGet ? StructBinder.adaptGet(si.adaptGet) : void 0); + }; + /** + If struct description object si has a setter proxy, return it (a + function), else return undefined. + */ + const memberSetterProxy = function(si) { + return si.set || (si.adaptSet ? StructBinder.adaptSet(si.adaptSet) : void 0); + }; + /** + To be called by makeMemberWrapper() when si has a 'members' + member, i.e. is an embedded struct. This function sets up that + struct like any other and also sets up property accessor for + ctor.memberKey(name) which returns an instance of that new + StructType when the member is accessed. That instance wraps the + memory of the member's part of the containing C struct instance. + + That is, if struct Foo has member bar which is an inner struct + then: + + const f = new Foo; + const b = f.bar; + assert( b is-a StructType object ); + assert( b.pointer === f.b.pointer ); + + b will be disposed of when f() is. Calling b.dispose() will not + do any permanent harm, as the wrapper object will be recreated + when accessing f.bar, pointing to the same memory in f. + + The si.zeroOnDispose flag has no effect on embedded structs because + they wrap "external" memory, so do not own it, and are thus never + freed, as such. + */ + const makeMemberStructWrapper = function callee(ctor, name, si) { + /** + Where we store inner-struct member proxies. Keys are a + combination of the parent object's pointer address and the + property's name. The values are StructType instances. + */ + const __innerStructs = callee.innerStructs ??= /* @__PURE__ */ new Map(); + const key = ctor.memberKey(name); + if (void 0 !== si.signature) toss("'signature' cannot be used on an embedded struct (", ctor.structName, ".", key, ")."); + if (memberSetterProxy(si)) toss("'set' and 'adaptSet' are not permitted for nested struct members."); + si.structName ??= ctor.structName + "::" + name; + si.key = key; + si.name = name; + si.constructor = this.call(this, si.structName, si); + const getterProxy = memberGetterProxy(si); + const prop = Object.assign(Object.create(null), { + configurable: false, + enumerable: false, + set: __propThrowOnSet(ctor.structName, key), + get: function() { + const dbg = this.debugFlags.__flags; + const p = this.pointer; + const k = p + "." + key; + let s = __innerStructs.get(k); + if (dbg.getter) log("debug.getter: k =", k); + if (!s) { + s = new si.constructor(__ptrAdd(p, si.offset)); + __innerStructs.set(k, s); + this.addOnDispose(() => s.dispose()); + s.addOnDispose(() => __innerStructs.delete(k)); + } + if (getterProxy) s = getterProxy.apply(this, [s, key]); + if (dbg.getter) log("debug.getter: result =", s); + return s; + } + }); + Object.defineProperty(ctor.prototype, key, prop); + }; + /** + This is where most of the magic happens. + + Pass this a StructBinderImpl-generated constructor, a member + property name, and the struct member description object. It will + define property accessors for proto[memberKey] which read + from/write to memory in this.pointer. It modifies si to make + certain downstream operations simpler. + */ + const makeMemberWrapper = function f(ctor, name, si) { + si = __adaptStruct2(this, si); + if (si.members) return makeMemberStructWrapper.call(this, ctor, name, si); + if (!f.cache) { + f.cache = { + getters: {}, + setters: {}, + sw: {} + }; + const a = [ + "i", + "c", + "C", + "p", + "P", + "s", + "f", + "d", + "v()" + ]; + if (bigIntEnabled) a.push("j"); + a.forEach(function(v) { + f.cache.getters[v] = sigDVGetter(v); + f.cache.setters[v] = sigDVSetter(v); + f.cache.sw[v] = sigDVSetWrapper(v); + }); + f.sigCheck = function(obj, name, key, sig) { + if (Object.prototype.hasOwnProperty.call(obj, key)) toss(obj.structName, "already has a property named", key + "."); + looksLikeASig(sig) || toss("Malformed signature for", sPropName(obj.structName, name) + ":", sig); + }; + } + const key = ctor.memberKey(name); + f.sigCheck(ctor.prototype, name, key, si.signature); + si.key = key; + si.name = name; + const sigGlyph = sigLetter(si.signature); + const xPropName = sPropName(ctor.structName, key); + const dbg = ctor.debugFlags.__flags; + const getterProxy = memberGetterProxy(si); + const prop = Object.create(null); + prop.configurable = false; + prop.enumerable = false; + prop.get = function() { + /** + This getter proxy reads its value from the appropriate pointer + address in the heap. It knows where and how much to read based on + this.pointer, si.offset, and si.sizeof. + */ + if (dbg.getter) log("debug.getter:", f.cache.getters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof); + let rc = new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.getters[sigGlyph]](0, isLittleEndian); + if (getterProxy) rc = getterProxy.apply(this, [key, rc]); + if (dbg.getter) log("debug.getter:", xPropName, "result =", rc); + return rc; + }; + if (si.readOnly) prop.set = __propThrowOnSet(ctor.prototype.structName, key); + else { + const setterProxy = memberSetterProxy(si); + prop.set = function(v) { + /** + The converse of prop.get(), this encodes v into the appropriate + spot in the WASM heap. + */ + if (dbg.setter) log("debug.setter:", f.cache.setters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof, v); + if (!this.pointer) toss("Cannot set native property on a disposed", this.structName, "instance."); + if (setterProxy) v = setterProxy.apply(this, [key, v]); + if (null === v || void 0 === v) v = __NullPtr; + else if (isPtrSig(si.signature) && !__isPtr(v)) if (isAutoPtrSig(si.signature) && v instanceof StructType) { + v = v.pointer || __NullPtr; + if (dbg.setter) log("debug.setter:", xPropName, "resolved to", v); + } else toss("Invalid value for pointer-type", xPropName + "."); + new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian); + }; + } + Object.defineProperty(ctor.prototype, key, prop); + }; + /** + The main factory function which will be returned to the + caller. The third argument is structly for internal use. + + This level of indirection is to avoid that clients can pass a + third argument to this, as that's only for internal use. + + internalOpt options: + + - None right now. This is for potential use in recursion. + + Usages: + + StructBinder(string, obj [,optObj]); + StructBinder(obj); + */ + const StructBinderImpl = function StructBinderImpl(structName, si, opt = Object.create(null)) { + /** + StructCtor is the eventual return value of this function. We + need to populate this early on so that we can do some trickery + in feeding it through recursion. + + Uses: + + // heap-allocated: + const x = new StructCtor; + // externally-managed memory: + const y = new StructCtor( aPtrToACompatibleCStruct ); + + or, more recently: + + const z = new StructCtor({ + extraBytes: [int=0] extra bytes to allocate after the struct + + wrap: [aPtrToACompatibleCStruct=undefined]. If provided, this + instance waps, but does not (by default) own the memory, else + a new instance is allocated from the WASM heap. + + ownsPointer: true if this object takes over ownership of + wrap. + + zeroOnDispose: [bool=StructCtor.structInfo.zeroOnDispose] + + autoCalcSizeOffset: [bool=false] Automatically calculate + sizeof an offset. This is fine for pure-JS structs (which + probably aren't useful beyond testing of Jaccwabyt) but it's + dangerous to use with actual WASM objects because we cannot + be guaranteed to have the same memory layout as an ostensibly + matching C struct. This applies recursively to all children + of the struct description. + + // TODO? Per-instance overrides of the struct-level flags? + + get: (k,v)=>v, + set: (k,v)=>v, + adaptGet: string, + adaptSet: string + + // That wouldn't fit really well right now, apparently. + }); + + */ + const StructCtor = function StructCtor(arg) { + if (!(this instanceof StructCtor)) toss("The", structName, "constructor may only be called via 'new'."); + __allocStruct(StructCtor, this, ...arguments); + }; + const self = this; + /** + "Convert" struct description x to a struct description, if + needed. This expands adaptStruct() mappings and transforms + {memberName:signatureString} signature syntax to object form. + */ + const ads = (x) => { + return "string" === typeof x && looksLikeASig(x) ? { signature: x } : __adaptStruct2(self, x); + }; + if (1 === arguments.length) { + si = ads(structName); + structName = si.structName || si.name; + } else if (2 === arguments.length) { + si = ads(si); + si.name ??= structName; + } else si = ads(si); + structName ??= si.structName; + structName ??= opt.structName; + if (!structName) toss("One of 'name' or 'structName' are required."); + if (si.adapt) { + Object.keys(si.adapt.struct || {}).forEach((k) => { + __adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]); + }); + Object.keys(si.adapt.set || {}).forEach((k) => { + __adaptSet.call(StructBinderImpl, k, si.adapt.set[k]); + }); + Object.keys(si.adapt.get || {}).forEach((k) => { + __adaptGet.call(StructBinderImpl, k, si.adapt.get[k]); + }); + } + if (!si.members && !si.sizeof) si.sizeof = sigSize(si.signature); + const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags)); + Object.defineProperties(StructCtor, { + debugFlags, + isA: rop((v) => v instanceof StructCtor), + memberKey: __memberKeyProp, + memberKeys: __structMemberKeys, + structInfo: rop(si), + structName: rop(structName), + ptrAdd: rop(__ptrAdd) + }); + StructCtor.prototype = new StructType(structName, si, rop); + Object.defineProperties(StructCtor.prototype, { + debugFlags, + constructor: rop(StructCtor), + ptrAdd: rop(__ptrAddSelf) + }); + let lastMember = false; + let offset = 0; + const autoCalc = !!si.autoCalcSizeOffset; + if (!autoCalc) { + if (!si.sizeof) toss(structName, "description is missing its sizeof property."); + si.offset ??= 0; + } else si.offset ??= 0; + Object.keys(si.members || {}).forEach((k) => { + let m = ads(si.members[k]); + if (!m.members && !m.sizeof) { + m.sizeof = sigSize(m.signature); + if (!m.sizeof) toss(sPropName(structName, k), "is missing a sizeof property.", m); + } + if (void 0 === m.offset) if (autoCalc) m.offset = offset; + else toss(sPropName(structName, k), "is missing its offset.", JSON.stringify(m)); + si.members[k] = m; + if (!lastMember || lastMember.offset < m.offset) lastMember = m; + const oldAutoCalc = !!m.autoCalc; + if (autoCalc) m.autoCalcSizeOffset = true; + makeMemberWrapper.call(self, StructCtor, k, m); + if (oldAutoCalc) m.autoCalcSizeOffset = true; + else delete m.autoCalcSizeOffset; + offset += m.sizeof; + }); + if (!lastMember) toss("No member property descriptions found."); + if (!si.sizeof) si.sizeof = offset; + if (si.sizeof === 1) si.signature === "c" || si.signature === "C" || toss("Unexpected sizeof==1 member", sPropName(structName, k), "with signature", si.signature); + else { + if (0 !== si.sizeof % 4) { + console.warn("Invalid struct member description", si); + toss(structName, "sizeof is not aligned. sizeof=" + si.sizeof); + } + if (0 !== si.offset % 4) { + console.warn("Invalid struct member description", si); + toss(structName, "offset is not aligned. offset=" + si.offset); + } + } + if (si.sizeof < offset) { + console.warn("Suspect struct description:", si, "offset =", offset); + toss("Mismatch in the calculated vs. the provided sizeof/offset info.", "Expected sizeof", offset, "but got", si.sizeof, "for", si); + } + delete si.autoCalcSizeOffset; + return StructCtor; + }; + const StructBinder = function StructBinder(structName, structInfo) { + return 1 == arguments.length ? StructBinderImpl.call(StructBinder, structName) : StructBinderImpl.call(StructBinder, structName, structInfo); + }; + StructBinder.StructType = StructType; + StructBinder.config = config; + StructBinder.allocCString = __allocCString; + StructBinder.adaptGet = __adaptGet; + StructBinder.adaptSet = __adaptSet; + StructBinder.adaptStruct = __adaptStruct; + StructBinder.ptrAdd = __ptrAdd; + if (!StructBinder.debugFlags) StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags); + return StructBinder; + }; + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + "use strict"; + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; + globalThis.WhWasmUtilInstaller(wasm); + delete globalThis.WhWasmUtilInstaller; + /** + Signatures for the WASM-exported C-side functions. Each entry + is an array with 2+ elements: + + [ "c-side name", + "result type" (wasm.xWrap() syntax), + [arg types in xWrap() syntax] + // ^^^ this needn't strictly be an array: it can be subsequent + // elements instead: [x,y,z] is equivalent to x,y,z + ] + + Support for the API-specific data types in the result/argument + type strings gets plugged in at a later phase in the API + initialization process. + */ + const bindingSignatures = { + core: [ + [ + "sqlite3_aggregate_context", + "void*", + "sqlite3_context*", + "int" + ], + [ + "sqlite3_bind_double", + "int", + "sqlite3_stmt*", + "int", + "f64" + ], + [ + "sqlite3_bind_int", + "int", + "sqlite3_stmt*", + "int", + "int" + ], + [ + "sqlite3_bind_null", + void 0, + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_bind_parameter_count", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_bind_parameter_index", + "int", + "sqlite3_stmt*", + "string" + ], + [ + "sqlite3_bind_parameter_name", + "string", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_bind_pointer", + "int", + "sqlite3_stmt*", + "int", + "*", + "string:static", + "*" + ], + [ + "sqlite3_busy_handler", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + signature: "i(pi)", + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_busy_timeout", + "int", + "sqlite3*", + "int" + ], + [ + "sqlite3_changes", + "int", + "sqlite3*" + ], + [ + "sqlite3_clear_bindings", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_collation_needed", + "int", + "sqlite3*", + "*", + "*" + ], + [ + "sqlite3_column_blob", + "*", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_bytes", + "int", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_count", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_column_decltype", + "string", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_double", + "f64", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_int", + "int", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_name", + "string", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_type", + "int", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_column_value", + "sqlite3_value*", + "sqlite3_stmt*", + "int" + ], + [ + "sqlite3_commit_hook", + "void*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_commit_hook", + signature: "i(p)", + contextKey: (argv) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_compileoption_get", + "string", + "int" + ], + [ + "sqlite3_compileoption_used", + "int", + "string" + ], + [ + "sqlite3_complete", + "int", + "string:flexible" + ], + [ + "sqlite3_context_db_handle", + "sqlite3*", + "sqlite3_context*" + ], + [ + "sqlite3_data_count", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_db_filename", + "string", + "sqlite3*", + "string" + ], + [ + "sqlite3_db_handle", + "sqlite3*", + "sqlite3_stmt*" + ], + [ + "sqlite3_db_name", + "string", + "sqlite3*", + "int" + ], + [ + "sqlite3_db_readonly", + "int", + "sqlite3*", + "string" + ], + [ + "sqlite3_db_status", + "int", + "sqlite3*", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_errcode", + "int", + "sqlite3*" + ], + [ + "sqlite3_errmsg", + "string", + "sqlite3*" + ], + [ + "sqlite3_error_offset", + "int", + "sqlite3*" + ], + [ + "sqlite3_errstr", + "string", + "int" + ], + [ + "sqlite3_exec", + "int", + [ + "sqlite3*", + "string:flexible", + new wasm.xWrap.FuncPtrAdapter({ + signature: "i(pipp)", + bindScope: "transient", + callProxy: (callback) => { + let aNames; + return (pVoid, nCols, pColVals, pColNames) => { + try { + const aVals = wasm.cArgvToJs(nCols, pColVals); + if (!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); + return callback(aVals, aNames) | 0; + } catch (e) { + return e.resultCode || capi.SQLITE_ERROR; + } + }; + } + }), + "*", + "**" + ] + ], + [ + "sqlite3_expanded_sql", + "string", + "sqlite3_stmt*" + ], + [ + "sqlite3_extended_errcode", + "int", + "sqlite3*" + ], + [ + "sqlite3_extended_result_codes", + "int", + "sqlite3*", + "int" + ], + [ + "sqlite3_file_control", + "int", + "sqlite3*", + "string", + "int", + "*" + ], + [ + "sqlite3_finalize", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_free", + void 0, + "*" + ], + [ + "sqlite3_get_autocommit", + "int", + "sqlite3*" + ], + [ + "sqlite3_get_auxdata", + "*", + "sqlite3_context*", + "int" + ], + ["sqlite3_initialize", void 0], + [ + "sqlite3_interrupt", + void 0, + "sqlite3*" + ], + [ + "sqlite3_is_interrupted", + "int", + "sqlite3*" + ], + ["sqlite3_keyword_count", "int"], + [ + "sqlite3_keyword_name", + "int", + [ + "int", + "**", + "*" + ] + ], + [ + "sqlite3_keyword_check", + "int", + ["string", "int"] + ], + ["sqlite3_libversion", "string"], + ["sqlite3_libversion_number", "int"], + [ + "sqlite3_limit", + "int", + [ + "sqlite3*", + "int", + "int" + ] + ], + [ + "sqlite3_malloc", + "*", + "int" + ], + [ + "sqlite3_next_stmt", + "sqlite3_stmt*", + ["sqlite3*", "sqlite3_stmt*"] + ], + [ + "sqlite3_open", + "int", + "string", + "*" + ], + [ + "sqlite3_open_v2", + "int", + "string", + "*", + "int", + "string" + ], + [ + "sqlite3_realloc", + "*", + "*", + "int" + ], + [ + "sqlite3_reset", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_result_blob", + void 0, + "sqlite3_context*", + "*", + "int", + "*" + ], + [ + "sqlite3_result_double", + void 0, + "sqlite3_context*", + "f64" + ], + [ + "sqlite3_result_error", + void 0, + "sqlite3_context*", + "string", + "int" + ], + [ + "sqlite3_result_error_code", + void 0, + "sqlite3_context*", + "int" + ], + [ + "sqlite3_result_error_nomem", + void 0, + "sqlite3_context*" + ], + [ + "sqlite3_result_error_toobig", + void 0, + "sqlite3_context*" + ], + [ + "sqlite3_result_int", + void 0, + "sqlite3_context*", + "int" + ], + [ + "sqlite3_result_null", + void 0, + "sqlite3_context*" + ], + [ + "sqlite3_result_pointer", + void 0, + "sqlite3_context*", + "*", + "string:static", + "*" + ], + [ + "sqlite3_result_subtype", + void 0, + "sqlite3_value*", + "int" + ], + [ + "sqlite3_result_text", + void 0, + "sqlite3_context*", + "string", + "int", + "*" + ], + [ + "sqlite3_result_zeroblob", + void 0, + "sqlite3_context*", + "int" + ], + [ + "sqlite3_rollback_hook", + "void*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_rollback_hook", + signature: "v(p)", + contextKey: (argv) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_set_auxdata", + void 0, + [ + "sqlite3_context*", + "int", + "*", + "*" + ] + ], + [ + "sqlite3_set_errmsg", + "int", + "sqlite3*", + "int", + "string" + ], + ["sqlite3_shutdown", void 0], + ["sqlite3_sourceid", "string"], + [ + "sqlite3_sql", + "string", + "sqlite3_stmt*" + ], + [ + "sqlite3_status", + "int", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_step", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_stmt_busy", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_stmt_readonly", + "int", + "sqlite3_stmt*" + ], + [ + "sqlite3_stmt_status", + "int", + "sqlite3_stmt*", + "int", + "int" + ], + [ + "sqlite3_strglob", + "int", + "string", + "string" + ], + [ + "sqlite3_stricmp", + "int", + "string", + "string" + ], + [ + "sqlite3_strlike", + "int", + "string", + "string", + "int" + ], + [ + "sqlite3_strnicmp", + "int", + "string", + "string", + "int" + ], + [ + "sqlite3_table_column_metadata", + "int", + "sqlite3*", + "string", + "string", + "string", + "**", + "**", + "*", + "*", + "*" + ], + [ + "sqlite3_total_changes", + "int", + "sqlite3*" + ], + [ + "sqlite3_trace_v2", + "int", + [ + "sqlite3*", + "int", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_trace_v2::callback", + signature: "i(ippp)", + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ], + [ + "sqlite3_txn_state", + "int", + ["sqlite3*", "string"] + ], + [ + "sqlite3_uri_boolean", + "int", + "sqlite3_filename", + "string", + "int" + ], + [ + "sqlite3_uri_key", + "string", + "sqlite3_filename", + "int" + ], + [ + "sqlite3_uri_parameter", + "string", + "sqlite3_filename", + "string" + ], + [ + "sqlite3_user_data", + "void*", + "sqlite3_context*" + ], + [ + "sqlite3_value_blob", + "*", + "sqlite3_value*" + ], + [ + "sqlite3_value_bytes", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_double", + "f64", + "sqlite3_value*" + ], + [ + "sqlite3_value_dup", + "sqlite3_value*", + "sqlite3_value*" + ], + [ + "sqlite3_value_free", + void 0, + "sqlite3_value*" + ], + [ + "sqlite3_value_frombind", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_int", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_nochange", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_numeric_type", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_pointer", + "*", + "sqlite3_value*", + "string:static" + ], + [ + "sqlite3_value_subtype", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_value_type", + "int", + "sqlite3_value*" + ], + [ + "sqlite3_vfs_find", + "*", + "string" + ], + [ + "sqlite3_vfs_register", + "int", + "sqlite3_vfs*", + "int" + ], + [ + "sqlite3_vfs_unregister", + "int", + "sqlite3_vfs*" + ] + ], + int64: [ + [ + "sqlite3_bind_int64", + "int", + [ + "sqlite3_stmt*", + "int", + "i64" + ] + ], + [ + "sqlite3_changes64", + "i64", + ["sqlite3*"] + ], + [ + "sqlite3_column_int64", + "i64", + ["sqlite3_stmt*", "int"] + ], + [ + "sqlite3_deserialize", + "int", + "sqlite3*", + "string", + "*", + "i64", + "i64", + "int" + ], + [ + "sqlite3_last_insert_rowid", + "i64", + ["sqlite3*"] + ], + [ + "sqlite3_malloc64", + "*", + "i64" + ], + [ + "sqlite3_msize", + "i64", + "*" + ], + [ + "sqlite3_overload_function", + "int", + [ + "sqlite3*", + "string", + "int" + ] + ], + [ + "sqlite3_realloc64", + "*", + "*", + "i64" + ], + [ + "sqlite3_result_int64", + void 0, + "*", + "i64" + ], + [ + "sqlite3_result_zeroblob64", + "int", + "*", + "i64" + ], + [ + "sqlite3_serialize", + "*", + "sqlite3*", + "string", + "*", + "int" + ], + [ + "sqlite3_set_last_insert_rowid", + void 0, + ["sqlite3*", "i64"] + ], + [ + "sqlite3_status64", + "int", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_db_status64", + "int", + "sqlite3*", + "int", + "*", + "*", + "int" + ], + [ + "sqlite3_total_changes64", + "i64", + ["sqlite3*"] + ], + [ + "sqlite3_update_hook", + "*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_update_hook::callback", + signature: "v(pippj)", + contextKey: (argv) => argv[0], + callProxy: (callback) => { + return (p, op, z0, z1, rowid) => { + callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid); + }; + } + }), + "*" + ] + ], + [ + "sqlite3_uri_int64", + "i64", + [ + "sqlite3_filename", + "string", + "i64" + ] + ], + [ + "sqlite3_value_int64", + "i64", + "sqlite3_value*" + ] + ], + wasmInternal: [ + [ + "sqlite3__wasm_db_reset", + "int", + "sqlite3*" + ], + [ + "sqlite3__wasm_db_vfs", + "sqlite3_vfs*", + "sqlite3*", + "string" + ], + [ + "sqlite3__wasm_vfs_create_file", + "int", + "sqlite3_vfs*", + "string", + "*", + "int" + ], + [ + "sqlite3__wasm_posix_create_file", + "int", + "string", + "*", + "int" + ], + [ + "sqlite3__wasm_vfs_unlink", + "int", + "sqlite3_vfs*", + "string" + ], + [ + "sqlite3__wasm_qfmt_token", + "string:dealloc", + "string", + "int" + ] + ] + }; + if (!!wasm.exports.sqlite3_progress_handler) bindingSignatures.core.push([ + "sqlite3_progress_handler", + void 0, + [ + "sqlite3*", + "int", + new wasm.xWrap.FuncPtrAdapter({ + name: "xProgressHandler", + signature: "i(p)", + bindScope: "context", + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ]); + if (!!wasm.exports.sqlite3_stmt_explain) bindingSignatures.core.push([ + "sqlite3_stmt_explain", + "int", + "sqlite3_stmt*", + "int" + ], [ + "sqlite3_stmt_isexplain", + "int", + "sqlite3_stmt*" + ]); + if (!!wasm.exports.sqlite3_set_authorizer) bindingSignatures.core.push([ + "sqlite3_set_authorizer", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_set_authorizer::xAuth", + signature: "i(pissss)", + contextKey: (argv, argIndex) => argv[0], + callProxy: (callback) => { + return (pV, iCode, s0, s1, s2, s3) => { + try { + s0 = s0 && wasm.cstrToJs(s0); + s1 = s1 && wasm.cstrToJs(s1); + s2 = s2 && wasm.cstrToJs(s2); + s3 = s3 && wasm.cstrToJs(s3); + return callback(pV, iCode, s0, s1, s2, s3) | 0; + } catch (e) { + return e.resultCode || capi.SQLITE_ERROR; + } + }; + } + }), + "*" + ] + ]); + if (!!wasm.exports.sqlite3_column_origin_name) bindingSignatures.core.push([ + "sqlite3_column_database_name", + "string", + "sqlite3_stmt*", + "int" + ], [ + "sqlite3_column_origin_name", + "string", + "sqlite3_stmt*", + "int" + ], [ + "sqlite3_column_table_name", + "string", + "sqlite3_stmt*", + "int" + ]); + if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_declare_vtab) bindingSignatures.int64.push([ + "sqlite3_create_module", + "int", + [ + "sqlite3*", + "string", + "sqlite3_module*", + "*" + ] + ], [ + "sqlite3_create_module_v2", + "int", + [ + "sqlite3*", + "string", + "sqlite3_module*", + "*", + "*" + ] + ], [ + "sqlite3_declare_vtab", + "int", + ["sqlite3*", "string:flexible"] + ], [ + "sqlite3_drop_modules", + "int", + ["sqlite3*", "**"] + ], [ + "sqlite3_vtab_collation", + "string", + "sqlite3_index_info*", + "int" + ], [ + "sqlite3_vtab_distinct", + "int", + "sqlite3_index_info*" + ], [ + "sqlite3_vtab_in", + "int", + "sqlite3_index_info*", + "int", + "int" + ], [ + "sqlite3_vtab_in_first", + "int", + "sqlite3_value*", + "**" + ], [ + "sqlite3_vtab_in_next", + "int", + "sqlite3_value*", + "**" + ], [ + "sqlite3_vtab_nochange", + "int", + "sqlite3_context*" + ], [ + "sqlite3_vtab_on_conflict", + "int", + "sqlite3*" + ], [ + "sqlite3_vtab_rhs_value", + "int", + "sqlite3_index_info*", + "int", + "**" + ]); + if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_preupdate_hook) bindingSignatures.int64.push([ + "sqlite3_preupdate_blobwrite", + "int", + "sqlite3*" + ], [ + "sqlite3_preupdate_count", + "int", + "sqlite3*" + ], [ + "sqlite3_preupdate_depth", + "int", + "sqlite3*" + ], [ + "sqlite3_preupdate_hook", + "*", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_preupdate_hook", + signature: "v(ppippjj)", + contextKey: (argv) => argv[0], + callProxy: (callback) => { + return (p, db, op, zDb, zTbl, iKey1, iKey2) => { + callback(p, db, op, wasm.cstrToJs(zDb), wasm.cstrToJs(zTbl), iKey1, iKey2); + }; + } + }), + "*" + ] + ], [ + "sqlite3_preupdate_new", + "int", + [ + "sqlite3*", + "int", + "**" + ] + ], [ + "sqlite3_preupdate_old", + "int", + [ + "sqlite3*", + "int", + "**" + ] + ]); + if (wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add && !!wasm.exports.sqlite3session_create && !!wasm.exports.sqlite3_preupdate_hook) { + /** + FuncPtrAdapter options for session-related callbacks with the + native signature "i(ps)". This proxy converts the 2nd argument + from a C string to a JS string before passing the arguments on + to the client-provided JS callback. + */ + const __ipsProxy = { + signature: "i(ps)", + callProxy: (callback) => { + return (p, s) => { + try { + return callback(p, wasm.cstrToJs(s)) | 0; + } catch (e) { + return e.resultCode || capi.SQLITE_ERROR; + } + }; + } + }; + bindingSignatures.int64.push([ + "sqlite3changegroup_add", + "int", + [ + "sqlite3_changegroup*", + "int", + "void*" + ] + ], [ + "sqlite3changegroup_add_strm", + "int", + [ + "sqlite3_changegroup*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changegroup_delete", + void 0, + ["sqlite3_changegroup*"] + ], [ + "sqlite3changegroup_new", + "int", + ["**"] + ], [ + "sqlite3changegroup_output", + "int", + [ + "sqlite3_changegroup*", + "int*", + "**" + ] + ], [ + "sqlite3changegroup_output_strm", + "int", + [ + "sqlite3_changegroup*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppi)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_apply", + "int", + [ + "sqlite3*", + "int", + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_apply_strm", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_apply_v2", + "int", + [ + "sqlite3*", + "int", + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_apply_v2_strm", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + bindScope: "transient", + ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_apply_v3", + "int", + [ + "sqlite3*", + "int", + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + signature: "i(pp)", + bindScope: "transient" + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_apply_v3_strm", + "int", + [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + signature: "i(pp)", + bindScope: "transient" + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xConflict", + signature: "i(pip)", + bindScope: "transient" + }), + "void*", + "**", + "int*", + "int" + ] + ], [ + "sqlite3changeset_concat", + "int", + [ + "int", + "void*", + "int", + "void*", + "int*", + "**" + ] + ], [ + "sqlite3changeset_concat_strm", + "int", + [ + new wasm.xWrap.FuncPtrAdapter({ + name: "xInputA", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInputB", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppi)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_conflict", + "int", + [ + "sqlite3_changeset_iter*", + "int", + "**" + ] + ], [ + "sqlite3changeset_finalize", + "int", + ["sqlite3_changeset_iter*"] + ], [ + "sqlite3changeset_fk_conflicts", + "int", + ["sqlite3_changeset_iter*", "int*"] + ], [ + "sqlite3changeset_invert", + "int", + [ + "int", + "void*", + "int*", + "**" + ] + ], [ + "sqlite3changeset_invert_strm", + "int", + [ + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppi)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_new", + "int", + [ + "sqlite3_changeset_iter*", + "int", + "**" + ] + ], [ + "sqlite3changeset_next", + "int", + ["sqlite3_changeset_iter*"] + ], [ + "sqlite3changeset_old", + "int", + [ + "sqlite3_changeset_iter*", + "int", + "**" + ] + ], [ + "sqlite3changeset_op", + "int", + [ + "sqlite3_changeset_iter*", + "**", + "int*", + "int*", + "int*" + ] + ], [ + "sqlite3changeset_pk", + "int", + [ + "sqlite3_changeset_iter*", + "**", + "int*" + ] + ], [ + "sqlite3changeset_start", + "int", + [ + "**", + "int", + "*" + ] + ], [ + "sqlite3changeset_start_strm", + "int", + [ + "**", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3changeset_start_v2", + "int", + [ + "**", + "int", + "*", + "int" + ] + ], [ + "sqlite3changeset_start_v2_strm", + "int", + [ + "**", + new wasm.xWrap.FuncPtrAdapter({ + name: "xInput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*", + "int" + ] + ], [ + "sqlite3session_attach", + "int", + ["sqlite3_session*", "string"] + ], [ + "sqlite3session_changeset", + "int", + [ + "sqlite3_session*", + "int*", + "**" + ] + ], [ + "sqlite3session_changeset_size", + "i64", + ["sqlite3_session*"] + ], [ + "sqlite3session_changeset_strm", + "int", + [ + "sqlite3_session*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3session_config", + "int", + ["int", "void*"] + ], [ + "sqlite3session_create", + "int", + [ + "sqlite3*", + "string", + "**" + ] + ], [ + "sqlite3session_diff", + "int", + [ + "sqlite3_session*", + "string", + "string", + "**" + ] + ], [ + "sqlite3session_enable", + "int", + ["sqlite3_session*", "int"] + ], [ + "sqlite3session_indirect", + "int", + ["sqlite3_session*", "int"] + ], [ + "sqlite3session_isempty", + "int", + ["sqlite3_session*"] + ], [ + "sqlite3session_memory_used", + "i64", + ["sqlite3_session*"] + ], [ + "sqlite3session_object_config", + "int", + [ + "sqlite3_session*", + "int", + "void*" + ] + ], [ + "sqlite3session_patchset", + "int", + [ + "sqlite3_session*", + "*", + "**" + ] + ], [ + "sqlite3session_patchset_strm", + "int", + [ + "sqlite3_session*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xOutput", + signature: "i(ppp)", + bindScope: "transient" + }), + "void*" + ] + ], [ + "sqlite3session_table_filter", + void 0, + [ + "sqlite3_session*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFilter", + ...__ipsProxy, + contextKey: (argv, argIndex) => argv[0] + }), + "*" + ] + ]); + } + /** + Prepare JS<->C struct bindings for the non-opaque struct types we + need... + */ + sqlite3.StructBinder = globalThis.Jaccwabyt({ + heap: wasm.heap8u, + alloc: wasm.alloc, + dealloc: wasm.dealloc, + bigIntEnabled: wasm.bigIntEnabled, + pointerIR: wasm.ptr.ir, + memberPrefix: "$" + }); + delete globalThis.Jaccwabyt; + { + const __xString = wasm.xWrap.argAdapter("string"); + wasm.xWrap.argAdapter("string:flexible", (v) => __xString(util.flexibleString(v))); + /** + The 'string:static' argument adapter treats its argument as + either... + + - WASM pointer: assumed to be a long-lived C-string which gets + returned as-is. + + - Anything else: gets coerced to a JS string for use as a map + key. If a matching entry is found (as described next), it is + returned, else wasm.allocCString() is used to create a a new + string, map its pointer to a copy of (''+v) for the remainder + of the application's life, and returns that pointer value for + this call and all future calls which are passed a + string-equivalent argument. + + Use case: sqlite3_bind_pointer(), sqlite3_result_pointer(), and + sqlite3_value_pointer() call for "a static string and + preferably a string literal". This converter is used to ensure + that the string value seen by those functions is long-lived and + behaves as they need it to, at the cost of a one-time leak of + each distinct key. + */ + wasm.xWrap.argAdapter("string:static", function(v) { + if (wasm.isPtr(v)) return v; + v = "" + v; + return this[v] || (this[v] = wasm.allocCString(v)); + }.bind(Object.create(null))); + /** + Add some descriptive xWrap() aliases for '*' intended to (A) + improve readability/correctness of bindingSignatures and (B) + provide automatic conversion from higher-level representations, + e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via (capi.sqlite3_vfs + instance).pointer. + */ + const __xArgPtr = wasm.xWrap.argAdapter("*"); + const nilType = function() {}; + wasm.xWrap.argAdapter("sqlite3_filename", __xArgPtr)("sqlite3_context*", __xArgPtr)("sqlite3_value*", __xArgPtr)("void*", __xArgPtr)("sqlite3_changegroup*", __xArgPtr)("sqlite3_changeset_iter*", __xArgPtr)("sqlite3_session*", __xArgPtr)("sqlite3_stmt*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.Stmt || nilType) ? v.pointer : v))("sqlite3*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.DB || nilType) ? v.pointer : v))("sqlite3_vfs*", (v) => { + if ("string" === typeof v) return capi.sqlite3_vfs_find(v) || sqlite3.SQLite3Error.toss(capi.SQLITE_NOTFOUND, "Unknown sqlite3_vfs name:", v); + return __xArgPtr(v instanceof (capi.sqlite3_vfs || nilType) ? v.pointer : v); + }); + if (wasm.exports.sqlite3_declare_vtab) wasm.xWrap.argAdapter("sqlite3_index_info*", (v) => __xArgPtr(v instanceof (capi.sqlite3_index_info || nilType) ? v.pointer : v))("sqlite3_module*", (v) => __xArgPtr(v instanceof (capi.sqlite3_module || nilType) ? v.pointer : v)); + /** + Alias `T*` to `*` for return type conversions for common T + types, primarily to improve legibility of their binding + signatures. + */ + const __xRcPtr = wasm.xWrap.resultAdapter("*"); + wasm.xWrap.resultAdapter("sqlite3*", __xRcPtr)("sqlite3_context*", __xRcPtr)("sqlite3_stmt*", __xRcPtr)("sqlite3_value*", __xRcPtr)("sqlite3_vfs*", __xRcPtr)("void*", __xRcPtr); + /** + Populate api object with sqlite3_...() by binding the "raw" wasm + exports into type-converting proxies using wasm.xWrap(). + */ + for (const e of bindingSignatures.core) capi[e[0]] = wasm.xWrap.apply(null, e); + for (const e of bindingSignatures.wasmInternal) util[e[0]] = wasm.xWrap.apply(null, e); + for (const e of bindingSignatures.int64) capi[e[0]] = wasm.bigIntEnabled ? wasm.xWrap.apply(null, e) : () => toss(e[0] + "() is unavailable due to lack", "of BigInt support in this build."); + delete bindingSignatures.core; + delete bindingSignatures.int64; + delete bindingSignatures.wasmInternal; + /** + Sets the given db's error state. Accepts: + + - (sqlite3*, int code, string msg) + - (sqlite3*, Error e [,string msg = ''+e]) + + If passed a WasmAllocError, the message is ignored and the + result code is SQLITE_NOMEM. If passed any other Error type, + the result code defaults to SQLITE_ERROR unless the Error + object has a resultCode property, in which case that is used + (e.g. SQLite3Error has that). If passed a non-WasmAllocError + exception, the message string defaults to ''+theError. + + Returns either the final result code, capi.SQLITE_NOMEM if + setting the message string triggers an OOM, or + capi.SQLITE_MISUSE if pDb is NULL or invalid (with the caveat + that behavior in the later case is undefined if pDb is not + "valid enough"). + + Pass (pDb,0,0) to clear the error state. + */ + util.sqlite3__wasm_db_error = function(pDb, resultCode, message) { + if (!pDb) return capi.SQLITE_MISUSE; + if (resultCode instanceof sqlite3.WasmAllocError) { + resultCode = capi.SQLITE_NOMEM; + message = 0; + } else if (resultCode instanceof Error) { + message = message || "" + resultCode; + resultCode = resultCode.resultCode || capi.SQLITE_ERROR; + } + return capi.sqlite3_set_errmsg(pDb, resultCode, message) || resultCode; + }; + } + { + const cJson = wasm.xCall("sqlite3__wasm_enum_json"); + if (!cJson) toss("Maintenance required: increase sqlite3__wasm_enum_json()'s", "static buffer size!"); + wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); + const defineGroups = [ + "access", + "authorizer", + "blobFinalizers", + "changeset", + "config", + "dataTypes", + "dbConfig", + "dbStatus", + "encodings", + "fcntl", + "flock", + "ioCap", + "limits", + "openFlags", + "prepareFlags", + "resultCodes", + "sqlite3Status", + "stmtStatus", + "syncFlags", + "trace", + "txnState", + "udfFlags", + "version" + ]; + if (wasm.bigIntEnabled) defineGroups.push("serialize", "session", "vtab"); + for (const t of defineGroups) for (const e of Object.entries(wasm.ctype[t])) capi[e[0]] = e[1]; + if (!wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)) toss("Internal error: cannot resolve exported function", "entry SQLITE_WASM_DEALLOC (==" + capi.SQLITE_WASM_DEALLOC + ")."); + const __rcMap = Object.create(null); + for (const e of Object.entries(wasm.ctype["resultCodes"])) __rcMap[e[1]] = e[0]; + /** + For the given integer, returns the SQLITE_xxx result code as a + string, or undefined if no such mapping is found. + */ + capi.sqlite3_js_rc_str = (rc) => __rcMap[rc]; + const notThese = Object.assign(Object.create(null), { + WasmTestStruct: true, + sqlite3_index_info: !wasm.bigIntEnabled, + sqlite3_index_constraint: !wasm.bigIntEnabled, + sqlite3_index_orderby: !wasm.bigIntEnabled, + sqlite3_index_constraint_usage: !wasm.bigIntEnabled + }); + for (const s of wasm.ctype.structs) if (!notThese[s.name]) capi[s.name] = sqlite3.StructBinder(s); + if (capi.sqlite3_index_info) { + for (const k of [ + "sqlite3_index_constraint", + "sqlite3_index_orderby", + "sqlite3_index_constraint_usage" + ]) { + capi.sqlite3_index_info[k] = capi[k]; + delete capi[k]; + } + capi.sqlite3_vtab_config = wasm.xWrap("sqlite3__wasm_vtab_config", "int", [ + "sqlite3*", + "int", + "int" + ]); + } + } + /** + Internal helper to assist in validating call argument counts in + the hand-written sqlite3_xyz() wrappers. We do this only for + consistency with non-special-case wrappings. + */ + const __dbArgcMismatch = (pDb, f, n) => { + return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, f + "() requires " + n + " argument" + (1 === n ? "" : "s") + "."); + }; + /** Code duplication reducer for functions which take an encoding + argument and require SQLITE_UTF8. Sets the db error code to + SQLITE_FORMAT, installs a descriptive error message, + and returns SQLITE_FORMAT. */ + const __errEncoding = (pDb) => { + return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding."); + }; + /** + __dbCleanupMap is infrastructure for recording registration of + UDFs and collations so that sqlite3_close_v2() can clean up any + automated JS-to-WASM function conversions installed by those. + */ + const __argPDb = (pDb) => wasm.xWrap.argAdapter("sqlite3*")(pDb); + const __argStr = (str) => wasm.isPtr(str) ? wasm.cstrToJs(str) : str; + const __dbCleanupMap = function(pDb, mode) { + pDb = __argPDb(pDb); + let m = this.dbMap.get(pDb); + if (!mode) { + this.dbMap.delete(pDb); + return m; + } else if (!m && mode > 0) this.dbMap.set(pDb, m = Object.create(null)); + return m; + }.bind(Object.assign(Object.create(null), { dbMap: /* @__PURE__ */ new Map() })); + __dbCleanupMap.addCollation = function(pDb, name) { + const m = __dbCleanupMap(pDb, 1); + if (!m.collation) m.collation = /* @__PURE__ */ new Set(); + m.collation.add(__argStr(name).toLowerCase()); + }; + __dbCleanupMap._addUDF = function(pDb, name, arity, map) { + name = __argStr(name).toLowerCase(); + let u = map.get(name); + if (!u) map.set(name, u = /* @__PURE__ */ new Set()); + u.add(arity < 0 ? -1 : arity); + }; + __dbCleanupMap.addFunction = function(pDb, name, arity) { + const m = __dbCleanupMap(pDb, 1); + if (!m.udf) m.udf = /* @__PURE__ */ new Map(); + this._addUDF(pDb, name, arity, m.udf); + }; + if (wasm.exports.sqlite3_create_window_function) __dbCleanupMap.addWindowFunc = function(pDb, name, arity) { + const m = __dbCleanupMap(pDb, 1); + if (!m.wudf) m.wudf = /* @__PURE__ */ new Map(); + this._addUDF(pDb, name, arity, m.wudf); + }; + /** + Intended to be called _only_ from sqlite3_close_v2(), + passed its non-0 db argument. + + This function frees up certain automatically-installed WASM + function bindings which were installed on behalf of the given db, + as those may otherwise leak. + + Notable caveat: this is only ever run via + sqlite3.capi.sqlite3_close_v2(). If a client, for whatever + reason, uses sqlite3.wasm.exports.sqlite3_close_v2() (the + function directly exported from WASM), this cleanup will not + happen. + + This is not a silver bullet for avoiding automation-related + leaks but represents "an honest effort." + + The issue being addressed here is covered at: + + https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr + */ + __dbCleanupMap.cleanup = function(pDb) { + pDb = __argPDb(pDb); + /** + Installing NULL functions in the C API will remove those + bindings. The FuncPtrAdapter which sits between us and the C + API will also treat that as an opportunity to + wasm.uninstallFunction() any WASM function bindings it has + installed for pDb. + */ + for (const obj of [ + ["sqlite3_busy_handler", 3], + ["sqlite3_commit_hook", 3], + ["sqlite3_preupdate_hook", 3], + ["sqlite3_progress_handler", 4], + ["sqlite3_rollback_hook", 3], + ["sqlite3_set_authorizer", 3], + ["sqlite3_trace_v2", 4], + ["sqlite3_update_hook", 3] + ]) { + const [name, arity] = obj; + if (!wasm.exports[name]) continue; + const closeArgs = [pDb]; + closeArgs.length = arity; + try { + capi[name](...closeArgs); + } catch (e) { + sqlite3.config.warn("close-time call of", name + "(", closeArgs, ") threw:", e); + } + } + const m = __dbCleanupMap(pDb, 0); + if (!m) return; + if (m.collation) { + for (const name of m.collation) try { + capi.sqlite3_create_collation_v2(pDb, name, capi.SQLITE_UTF8, 0, 0, 0); + } catch (e) {} + delete m.collation; + } + let i; + for (i = 0; i < 2; ++i) { + const fmap = i ? m.wudf : m.udf; + if (!fmap) continue; + const func = i ? capi.sqlite3_create_window_function : capi.sqlite3_create_function_v2; + for (const e of fmap) { + const name = e[0], arities = e[1]; + const fargs = [ + pDb, + name, + 0, + capi.SQLITE_UTF8, + 0, + 0, + 0, + 0, + 0 + ]; + if (i) fargs.push(0); + for (const arity of arities) try { + fargs[2] = arity; + func.apply(null, fargs); + } catch (e) {} + arities.clear(); + } + fmap.clear(); + } + delete m.udf; + delete m.wudf; + }; + { + const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*"); + capi.sqlite3_close_v2 = function(pDb) { + if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_close_v2", 1); + if (pDb) try { + __dbCleanupMap.cleanup(pDb); + } catch (e) {} + return __sqlite3CloseV2(pDb); + }; + } + if (capi.sqlite3session_create) { + const __sqlite3SessionDelete = wasm.xWrap("sqlite3session_delete", void 0, ["sqlite3_session*"]); + capi.sqlite3session_delete = function(pSession) { + if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3session_delete", 1); + else if (pSession) capi.sqlite3session_table_filter(pSession, 0, 0); + __sqlite3SessionDelete(pSession); + }; + } + { + const contextKey = (argv, argIndex) => { + return "argv[" + argIndex + "]:" + argv[0] + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); + }; + const __sqlite3CreateCollationV2 = wasm.xWrap("sqlite3_create_collation_v2", "int", [ + "sqlite3*", + "string", + "int", + "*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xCompare", + signature: "i(pipip)", + contextKey + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xDestroy", + signature: "v(p)", + contextKey + }) + ]); + /** + Works exactly like C's sqlite3_create_collation_v2() except that: + + 1) It returns capi.SQLITE_FORMAT if the 3rd argument contains + any encoding-related value other than capi.SQLITE_UTF8. No + other encodings are supported. As a special case, if the + bottom 4 bits of that argument are 0, SQLITE_UTF8 is + assumed. + + 2) It accepts JS functions for its function-pointer arguments, + for which it will install WASM-bound proxies. The bindings + are "permanent," in that they will stay in the WASM + environment until it shuts down unless the client calls this + again with the same collation name and a value of 0 or null + for the the function pointer(s). sqlite3_close_v2() will + also clean up such automatically-installed WASM functions. + + For consistency with the C API, it requires the same number of + arguments. It returns capi.SQLITE_MISUSE if passed any other + argument count. + + Returns 0 on success, non-0 on error, in which case the error + state of pDb (of type `sqlite3*` or argument-convertible to it) + may contain more information. + */ + capi.sqlite3_create_collation_v2 = function(pDb, zName, eTextRep, pArg, xCompare, xDestroy) { + if (6 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_collation_v2", 6); + else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; + else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); + try { + const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); + if (0 === rc && xCompare instanceof Function) __dbCleanupMap.addCollation(pDb, zName); + return rc; + } catch (e) { + return util.sqlite3__wasm_db_error(pDb, e); + } + }; + capi.sqlite3_create_collation = (pDb, zName, eTextRep, pArg, xCompare) => { + return 5 === arguments.length ? capi.sqlite3_create_collation_v2(pDb, zName, eTextRep, pArg, xCompare, 0) : __dbArgcMismatch(pDb, "sqlite3_create_collation", 5); + }; + } + { + /** FuncPtrAdapter for contextKey() for sqlite3_create_function() + and friends. */ + const contextKey = function(argv, argIndex) { + return argv[0] + ":" + (argv[2] < 0 ? -1 : argv[2]) + ":" + argIndex + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); + }; + /** + JS proxies for the various sqlite3_create[_window]_function() + callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter. + */ + const __cfProxy = Object.assign(Object.create(null), { + xInverseAndStep: { + signature: "v(pip)", + contextKey, + callProxy: (callback) => { + return (pCtx, argc, pArgv) => { + try { + callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)); + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xFinalAndValue: { + signature: "v(p)", + contextKey, + callProxy: (callback) => { + return (pCtx) => { + try { + capi.sqlite3_result_js(pCtx, callback(pCtx)); + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xFunc: { + signature: "v(pip)", + contextKey, + callProxy: (callback) => { + return (pCtx, argc, pArgv) => { + try { + capi.sqlite3_result_js(pCtx, callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))); + } catch (e) { + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xDestroy: { + signature: "v(p)", + contextKey, + callProxy: (callback) => { + return (pVoid) => { + try { + callback(pVoid); + } catch (e) { + console.error("UDF xDestroy method threw:", e); + } + }; + } + } + }); + const __sqlite3CreateFunction = wasm.xWrap("sqlite3_create_function_v2", "int", [ + "sqlite3*", + "string", + "int", + "int", + "*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xFunc", + ...__cfProxy.xFunc + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xStep", + ...__cfProxy.xInverseAndStep + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xFinal", + ...__cfProxy.xFinalAndValue + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xDestroy", + ...__cfProxy.xDestroy + }) + ]); + const __sqlite3CreateWindowFunction = wasm.exports.sqlite3_create_window_function ? wasm.xWrap("sqlite3_create_window_function", "int", [ + "sqlite3*", + "string", + "int", + "int", + "*", + new wasm.xWrap.FuncPtrAdapter({ + name: "xStep", + ...__cfProxy.xInverseAndStep + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xFinal", + ...__cfProxy.xFinalAndValue + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xValue", + ...__cfProxy.xFinalAndValue + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xInverse", + ...__cfProxy.xInverseAndStep + }), + new wasm.xWrap.FuncPtrAdapter({ + name: "xDestroy", + ...__cfProxy.xDestroy + }) + ]) : void 0; + capi.sqlite3_create_function_v2 = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_function_v2", f.length); + else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; + else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); + try { + const rc = __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy); + if (0 === rc && (xFunc instanceof Function || xStep instanceof Function || xFinal instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addFunction(pDb, funcName, nArg); + return rc; + } catch (e) { + console.error("sqlite3_create_function_v2() setup threw:", e); + return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); + } + }; + capi.sqlite3_create_function = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) { + return f.length === arguments.length ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, 0) : __dbArgcMismatch(pDb, "sqlite3_create_function", f.length); + }; + if (__sqlite3CreateWindowFunction) capi.sqlite3_create_window_function = function f(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_window_function", f.length); + else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; + else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); + try { + const rc = __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy); + if (0 === rc && (xStep instanceof Function || xFinal instanceof Function || xValue instanceof Function || xInverse instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); + return rc; + } catch (e) { + console.error("sqlite3_create_window_function() setup threw:", e); + return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); + } + }; + else delete capi.sqlite3_create_window_function; + /** + A _deprecated_ alias for capi.sqlite3_result_js() which + predates the addition of that function in the public API. + */ + capi.sqlite3_create_function_v2.udfSetResult = capi.sqlite3_create_function.udfSetResult = capi.sqlite3_result_js; + if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js; + /** + A _deprecated_ alias for capi.sqlite3_values_to_js() which + predates the addition of that function in the public API. + */ + capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_values_to_js; + if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js; + /** + A _deprecated_ alias for capi.sqlite3_result_error_js() which + predates the addition of that function in the public API. + */ + capi.sqlite3_create_function_v2.udfSetError = capi.sqlite3_create_function.udfSetError = capi.sqlite3_result_error_js; + if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js; + } + { + /** + Helper for string:flexible conversions which requires a + byte-length counterpart argument. Passed a value and its + ostensible length, this function returns [V,N], where V is + either v or a to-string transformed copy of v and N is either n + (if v is a WASM pointer, in which case n might be a BigInt), -1 + (if v is a string or Array), or the byte length of v (if it's a + byte array or ArrayBuffer). + */ + const __flexiString = (v, n) => { + if ("string" === typeof v) n = -1; + else if (util.isSQLableTypedArray(v)) { + n = v.byteLength; + v = wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v); + } else if (Array.isArray(v)) { + v = v.join(""); + n = -1; + } + return [v, n]; + }; + /** + Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). + */ + const __prepare = { + basic: wasm.xWrap("sqlite3_prepare_v3", "int", [ + "sqlite3*", + "string", + "int", + "int", + "**", + "**" + ]), + full: wasm.xWrap("sqlite3_prepare_v3", "int", [ + "sqlite3*", + "*", + "int", + "int", + "**", + "**" + ]) + }; + capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail) { + if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_prepare_v3", f.length); + const [xSql, xSqlLen] = __flexiString(sql, Number(sqlLen)); + switch (typeof xSql) { + case "string": return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); + case typeof wasm.ptr.null: return __prepare.full(pDb, wasm.ptr.coerce(xSql), xSqlLen, prepFlags, ppStmt, pzTail); + default: return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3(). typeof=" + typeof xSql); + } + }; + capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail) { + return f.length === arguments.length ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) : __dbArgcMismatch(pDb, "sqlite3_prepare_v2", f.length); + }; + } + { + const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [ + "sqlite3_stmt*", + "int", + "string", + "int", + "*" + ]); + const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [ + "sqlite3_stmt*", + "int", + "*", + "int", + "*" + ]); + /** Documented in the capi object's initializer. */ + capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_text", f.length); + else if (wasm.isPtr(text) || null === text) return __bindText(pStmt, iCol, text, nText, xDestroy); + else if (text instanceof ArrayBuffer) text = new Uint8Array(text); + else if (Array.isArray(pMem)) text = pMem.join(""); + let p, n; + try { + if (util.isSQLableTypedArray(text)) { + p = wasm.allocFromTypedArray(text); + n = text.byteLength; + } else if ("string" === typeof text) [p, n] = wasm.allocCString(text); + else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_text()."); + return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); + } catch (e) { + wasm.dealloc(p); + return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); + } + }; + /** Documented in the capi object's initializer. */ + capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy) { + if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_blob", f.length); + else if (wasm.isPtr(pMem) || null === pMem) return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy); + else if (pMem instanceof ArrayBuffer) pMem = new Uint8Array(pMem); + else if (Array.isArray(pMem)) pMem = pMem.join(""); + let p, n; + try { + if (util.isBindableTypedArray(pMem)) { + p = wasm.allocFromTypedArray(pMem); + n = nMem >= 0 ? nMem : pMem.byteLength; + } else if ("string" === typeof pMem) [p, n] = wasm.allocCString(pMem); + else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_blob()."); + return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); + } catch (e) { + wasm.dealloc(p); + return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); + } + }; + } + if (!capi.sqlite3_column_text) { + const argStmt = wasm.xWrap.argAdapter("sqlite3_stmt*"), argInt = wasm.xWrap.argAdapter("int"), argValue = wasm.xWrap.argAdapter("sqlite3_value*"), newStr = (cstr, n) => wasm.typedArrayToString(wasm.heap8u(), Number(cstr), Number(cstr) + n); + capi.sqlite3_column_text = function(stmt, colIndex) { + const a0 = argStmt(stmt), a1 = argInt(colIndex); + const cstr = wasm.exports.sqlite3_column_text(a0, a1); + return cstr ? newStr(cstr, wasm.exports.sqlite3_column_bytes(a0, a1)) : null; + }; + capi.sqlite3_value_text = function(val) { + const a0 = argValue(val); + const cstr = wasm.exports.sqlite3_value_text(a0); + return cstr ? newStr(cstr, wasm.exports.sqlite3_value_bytes(a0)) : null; + }; + } + /** + Wraps a small subset of the C API's sqlite3_config() options. + Unsupported options trigger the return of capi.SQLITE_NOTFOUND. + Passing fewer than 2 arguments triggers return of + capi.SQLITE_MISUSE. + */ + capi.sqlite3_config = function(op, ...args) { + if (arguments.length < 2) return capi.SQLITE_MISUSE; + switch (op) { + case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: + case capi.SQLITE_CONFIG_MEMSTATUS: + case capi.SQLITE_CONFIG_SMALL_MALLOC: + case capi.SQLITE_CONFIG_SORTERREF_SIZE: + case capi.SQLITE_CONFIG_STMTJRNL_SPILL: + case capi.SQLITE_CONFIG_URI: return wasm.exports.sqlite3__wasm_config_i(op, args[0]); + case capi.SQLITE_CONFIG_LOOKASIDE: return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]); + case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: return wasm.exports.sqlite3__wasm_config_j(op, args[0]); + case capi.SQLITE_CONFIG_GETMALLOC: + case capi.SQLITE_CONFIG_GETMUTEX: + case capi.SQLITE_CONFIG_GETPCACHE2: + case capi.SQLITE_CONFIG_GETPCACHE: + case capi.SQLITE_CONFIG_HEAP: + case capi.SQLITE_CONFIG_LOG: + case capi.SQLITE_CONFIG_MALLOC: + case capi.SQLITE_CONFIG_MMAP_SIZE: + case capi.SQLITE_CONFIG_MULTITHREAD: + case capi.SQLITE_CONFIG_MUTEX: + case capi.SQLITE_CONFIG_PAGECACHE: + case capi.SQLITE_CONFIG_PCACHE2: + case capi.SQLITE_CONFIG_PCACHE: + case capi.SQLITE_CONFIG_PCACHE_HDRSZ: + case capi.SQLITE_CONFIG_PMASZ: + case capi.SQLITE_CONFIG_SERIALIZED: + case capi.SQLITE_CONFIG_SINGLETHREAD: + case capi.SQLITE_CONFIG_SQLLOG: + case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: + default: return capi.SQLITE_NOTFOUND; + } + }; + { + const __autoExtFptr = /* @__PURE__ */ new Set(); + capi.sqlite3_auto_extension = function(fPtr) { + if (fPtr instanceof Function) fPtr = wasm.installFunction("i(ppp)", fPtr); + else if (1 !== arguments.length || !wasm.isPtr(fPtr)) return capi.SQLITE_MISUSE; + const rc = wasm.exports.sqlite3_auto_extension(fPtr); + if (fPtr !== arguments[0]) if (0 === rc) __autoExtFptr.add(fPtr); + else wasm.uninstallFunction(fPtr); + return rc; + }; + capi.sqlite3_cancel_auto_extension = function(fPtr) { + if (!fPtr || 1 !== arguments.length || !wasm.isPtr(fPtr)) return 0; + return wasm.exports.sqlite3_cancel_auto_extension(fPtr); + }; + capi.sqlite3_reset_auto_extension = function() { + wasm.exports.sqlite3_reset_auto_extension(); + for (const fp of __autoExtFptr) wasm.uninstallFunction(fp); + __autoExtFptr.clear(); + }; + } + wasm.xWrap.FuncPtrAdapter.warnOnUse = true; + const StructBinder = sqlite3.StructBinder; + /** + Installs a StructBinder-bound function pointer member of the + given name and function in the given StructBinder.StructType + target object. + + It creates a WASM proxy for the given function and arranges for + that proxy to be cleaned up when tgt.dispose() is called. Throws + on the slightest hint of error, e.g. tgt is-not-a StructType, + name does not map to a struct-bound member, etc. + + As a special case, if the given function is a pointer, then + `wasm.functionEntry()` is used to validate that it is a known + function. If so, it is used as-is with no extra level of proxying + or cleanup, else an exception is thrown. It is legal to pass a + value of 0, indicating a NULL pointer, with the caveat that 0 + _is_ a legal function pointer in WASM but it will not be accepted + as such _here_. (Justification: the function at address zero must + be one which initially came from the WASM module, not a method we + want to bind to a virtual table or VFS.) + + This function returns a proxy for itself which is bound to tgt + and takes 2 args (name,func). That function returns the same + thing as this one, permitting calls to be chained. + + If called with only 1 arg, it has no side effects but returns a + func with the same signature as described above. + + ACHTUNG: because we cannot generically know how to transform JS + exceptions into result codes, the installed functions do no + automatic catching of exceptions. It is critical, to avoid + undefined behavior in the C layer, that methods mapped via + this function do not throw. The exception, as it were, to that + rule is... + + If applyArgcCheck is true then each JS function (as opposed to + function pointers) gets wrapped in a proxy which asserts that it + is passed the expected number of arguments, throwing if the + argument count does not match expectations. That is only intended + for dev-time usage for sanity checking, and may leave the C + environment in an undefined state. + */ + const installMethod = function callee(tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck) { + if (!(tgt instanceof StructBinder.StructType)) toss("Usage error: target object is-not-a StructType."); + else if (!(func instanceof Function) && !wasm.isPtr(func)) toss("Usage error: expecting a Function or WASM pointer to one."); + if (1 === arguments.length) return (n, f) => callee(tgt, n, f, applyArgcCheck); + if (!callee.argcProxy) { + callee.argcProxy = function(tgt, funcName, func, sig) { + return function(...args) { + if (func.length !== arguments.length) toss("Argument mismatch for", tgt.structInfo.name + "::" + funcName + ": Native signature is:", sig); + return func.apply(this, args); + }; + }; + callee.removeFuncList = function() { + if (this.ondispose.__removeFuncList) { + this.ondispose.__removeFuncList.forEach((v, ndx) => { + if (wasm.isPtr(v)) try { + wasm.uninstallFunction(v); + } catch (e) {} + }); + delete this.ondispose.__removeFuncList; + } + }; + } + const sigN = tgt.memberSignature(name); + if (sigN.length < 2) toss("Member", name, "does not have a function pointer signature:", sigN); + const memKey = tgt.memberKey(name); + const fProxy = applyArgcCheck && !wasm.isPtr(func) ? callee.argcProxy(tgt, memKey, func, sigN) : func; + if (wasm.isPtr(fProxy)) { + if (fProxy && !wasm.functionEntry(fProxy)) toss("Pointer", fProxy, "is not a WASM function table entry."); + tgt[memKey] = fProxy; + } else { + const pFunc = wasm.installFunction(fProxy, sigN); + tgt[memKey] = pFunc; + if (!tgt.ondispose || !tgt.ondispose.__removeFuncList) { + tgt.addOnDispose("ondispose.__removeFuncList handler", callee.removeFuncList); + tgt.ondispose.__removeFuncList = []; + } + tgt.ondispose.__removeFuncList.push(memKey, pFunc); + } + return (n, f) => callee(tgt, n, f, applyArgcCheck); + }; + installMethod.installMethodArgcCheck = false; + /** + Installs methods into the given StructBinder.StructType-type + instance. Each entry in the given methods object must map to a + known member of the given StructType, else an exception will be + triggered. See installMethod() for more details, including the + semantics of the 3rd argument. + + As an exception to the above, if any two or more methods in the + 2nd argument are the exact same function, installMethod() is + _not_ called for the 2nd and subsequent instances, and instead + those instances get assigned the same method pointer which is + created for the first instance. This optimization is primarily to + accommodate special handling of sqlite3_module::xConnect and + xCreate methods. + + On success, returns its first argument. Throws on error. + */ + const installMethods = function(structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck) { + const seen = /* @__PURE__ */ new Map(); + for (const k of Object.keys(methods)) { + const m = methods[k]; + const prior = seen.get(m); + if (prior) { + const mkey = structInstance.memberKey(k); + structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; + } else { + installMethod(structInstance, k, m, applyArgcCheck); + seen.set(m, k); + } + } + return structInstance; + }; + /** + Equivalent to calling installMethod(this,...arguments) with a + first argument of this object. If called with 1 or 2 arguments + and the first is an object, it's instead equivalent to calling + installMethods(this,...arguments). + */ + StructBinder.StructType.prototype.installMethod = function callee(name, func, applyArgcCheck = installMethod.installMethodArgcCheck) { + return arguments.length < 3 && name && "object" === typeof name ? installMethods(this, ...arguments) : installMethod(this, ...arguments); + }; + /** + Equivalent to calling installMethods() with a first argument + of this object. + */ + StructBinder.StructType.prototype.installMethods = function(methods, applyArgcCheck = installMethod.installMethodArgcCheck) { + return installMethods(this, methods, applyArgcCheck); + }; + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + const toss3 = (...args) => { + throw new sqlite3.SQLite3Error(...args); + }; + const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; + const outWrapper = function(f) { + return (...args) => f("sqlite3.oo1:", ...args); + }; + sqlite3.__isUnderTest ? outWrapper(console.debug.bind(console)) : outWrapper(sqlite3.config.debug); + sqlite3.__isUnderTest ? outWrapper(console.warn.bind(console)) : outWrapper(sqlite3.config.warn); + sqlite3.__isUnderTest ? outWrapper(console.error.bind(console)) : outWrapper(sqlite3.config.error); + /** + In order to keep clients from manipulating, perhaps + inadvertently, the underlying pointer values of DB and Stmt + instances, we'll gate access to them via the `pointer` property + accessor and store their real values in this map. Keys = DB/Stmt + objects, values = pointer values. This also unifies how those are + accessed, for potential use downstream via custom + wasm.xWrap() function signatures which know how to extract + it. + */ + const __ptrMap = /* @__PURE__ */ new WeakMap(); + /** + A Set of oo1.DB or oo1.Stmt objects which are proxies for + (sqlite3*) resp. (sqlite3_stmt*) pointers which themselves are + owned elsewhere. Objects in this Set do not own their underlying + handle and that handle must be guaranteed (by the client) to + outlive the proxy. DB.close()/Stmt.finalize() methods will remove + the object from this Set _instead_ of closing/finalizing the + pointer. These proxies are primarily intended as a way to briefly + wrap an (sqlite3[_stmt]*) object as an oo1.DB/Stmt without taking + over ownership, to take advantage of simplifies usage compared to + the C API while not imposing any change of ownership. + + See DB.wrapHandle() and Stmt.wrapHandle(). + */ + const __doesNotOwnHandle = /* @__PURE__ */ new Set(); + /** + Map of DB instances to objects, each object being a map of Stmt + wasm pointers to Stmt objects. + */ + const __stmtMap = /* @__PURE__ */ new WeakMap(); + /** If object opts has _its own_ property named p then that + property's value is returned, else dflt is returned. */ + const getOwnOption = (opts, p, dflt) => { + const d = Object.getOwnPropertyDescriptor(opts, p); + return d ? d.value : dflt; + }; + const checkSqlite3Rc = function(dbPtr, sqliteResultCode) { + if (sqliteResultCode) { + if (dbPtr instanceof DB) dbPtr = dbPtr.pointer; + toss3(sqliteResultCode, "sqlite3 result code", sqliteResultCode + ":", dbPtr ? capi.sqlite3_errmsg(dbPtr) : capi.sqlite3_errstr(sqliteResultCode)); + } + return arguments[0]; + }; + /** + sqlite3_trace_v2() callback which gets installed by the DB ctor + if its open-flags contain "t". + */ + const __dbTraceToConsole = wasm.installFunction("i(ippp)", function(t, c, p, x) { + if (capi.SQLITE_TRACE_STMT === t) console.log("SQL TRACE #" + ++this.counter, "via sqlite3@" + c + "[" + capi.sqlite3_db_filename(c, null) + "]", wasm.cstrToJs(x)); + }.bind({ counter: 0 })); + /** + A map of sqlite3_vfs pointers to SQL code or a callback function + to run when the DB constructor opens a database with the given + VFS. In the latter case, the call signature is + (theDbObject,sqlite3Namespace) and the callback is expected to + throw on error. + */ + const __vfsPostOpenCallback = Object.create(null); + /** + A proxy for DB class constructors. It must be called with the + being-construct DB object as its "this". See the DB constructor + for the argument docs. This is split into a separate function + in order to enable simple creation of special-case DB constructors, + e.g. JsStorageDb and OpfsDb. + + Expects to be passed a configuration object with the following + properties: + + - `.filename`: the db filename. It may be a special name like ":memory:" + or "". It may also be a URI-style name. + + - `.flags`: as documented in the DB constructor. + + - `.vfs`: as documented in the DB constructor. + + It also accepts those as the first 3 arguments. + + In non-default builds it may accept additional configuration + options. + */ + const dbCtorHelper = function ctor(...args) { + const opt = ctor.normalizeArgs(...args); + let pDb; + if (pDb = opt["sqlite3*"]) { + if (!opt["sqlite3*:takeOwnership"]) __doesNotOwnHandle.add(this); + this.filename = capi.sqlite3_db_filename(pDb, "main"); + } else { + let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags; + if ("string" !== typeof fn && !wasm.isPtr(fn) || "string" !== typeof flagsStr || vfsName && "string" !== typeof vfsName && !wasm.isPtr(vfsName)) { + sqlite3.config.error("Invalid DB ctor args", opt, arguments); + toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt); + } + let oflags = 0; + if (flagsStr.indexOf("c") >= 0) oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + if (flagsStr.indexOf("w") >= 0) oflags |= capi.SQLITE_OPEN_READWRITE; + if (0 === oflags) oflags |= capi.SQLITE_OPEN_READONLY; + oflags |= capi.SQLITE_OPEN_EXRESCODE; + const stack = wasm.pstack.pointer; + try { + const pPtr = wasm.pstack.allocPtr(); + let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || wasm.ptr.null); + pDb = wasm.peekPtr(pPtr); + checkSqlite3Rc(pDb, rc); + capi.sqlite3_extended_result_codes(pDb, 1); + if (flagsStr.indexOf("t") >= 0) capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, pDb); + } catch (e) { + if (pDb) capi.sqlite3_close_v2(pDb); + throw e; + } finally { + wasm.pstack.restore(stack); + } + this.filename = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn; + } + __ptrMap.set(this, pDb); + __stmtMap.set(this, Object.create(null)); + if (!opt["sqlite3*"]) try { + const postInitSql = __vfsPostOpenCallback[capi.sqlite3_js_db_vfs(pDb) || toss3("Internal error: cannot get VFS for new db handle.")]; + if (postInitSql) + /** + Reminder: if this db is encrypted and the client did _not_ pass + in the key, any init code will fail, causing the ctor to throw. + We don't actually know whether the db is encrypted, so we cannot + sensibly apply any heuristics which skip the init code only for + encrypted databases for which no key has yet been supplied. + */ + if (postInitSql instanceof Function) postInitSql(this, sqlite3); + else checkSqlite3Rc(pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)); + } catch (e) { + this.close(); + throw e; + } + }; + /** + Sets a callback which should be called after a db is opened with + the given sqlite3_vfs pointer. The 2nd argument must be a + function, which gets called with + (theOo1DbObject,sqlite3Namespace) at the end of the DB() + constructor. The function must throw on error, in which case the + db is closed and the exception is propagated. This function is + intended only for use by DB subclasses or sqlite3_vfs + implementations. + + Prior to 2024-07-22, it was legal to pass SQL code as the second + argument, but that can interfere with a client's ability to run + pragmas which must be run before anything else, namely (pragma + locking_mode=exclusive) for use with WAL mode. That capability + had only ever been used as an internal detail of the two OPFS + VFSes, and they no longer use it that way. + */ + dbCtorHelper.setVfsPostOpenCallback = function(pVfs, callback) { + if (!(callback instanceof Function)) toss3("dbCtorHelper.setVfsPostOpenCallback() should not be used with a non-function argument.", arguments); + __vfsPostOpenCallback[pVfs] = callback; + }; + /** + A helper for DB constructors. It accepts either a single + config-style object or up to 3 arguments (filename, dbOpenFlags, + dbVfsName). It returns a new object containing: + + { filename: ..., flags: ..., vfs: ... } + + If passed an object, any additional properties it has are copied + as-is into the new object. + */ + dbCtorHelper.normalizeArgs = function(filename = ":memory:", flags = "c", vfs = null) { + const arg = {}; + if (1 === arguments.length && arguments[0] && "object" === typeof arguments[0]) { + Object.assign(arg, arguments[0]); + if (void 0 === arg.flags) arg.flags = "c"; + if (void 0 === arg.vfs) arg.vfs = null; + if (void 0 === arg.filename) arg.filename = ":memory:"; + } else { + arg.filename = filename; + arg.flags = flags; + arg.vfs = vfs; + } + return arg; + }; + /** + The DB class provides a high-level OO wrapper around an sqlite3 + db handle. + + The given db filename must be resolvable using whatever + filesystem layer (virtual or otherwise) is set up for the default + sqlite3 VFS or a VFS which can resolve it must be specified. + + The special sqlite3 db names ":memory:" and "" (temporary db) + have their normal special meanings here and need not resolve to + real filenames, but "" uses an on-storage temporary database and + requires that the VFS support that. + + The second argument specifies the open/create mode for the + database. It must be string containing a sequence of letters (in + any order, but case sensitive) specifying the mode: + + - "c": create if it does not exist, else fail if it does not + exist. Implies the "w" flag. + + - "w": write. Implies "r": a db cannot be write-only. + + - "r": read-only if neither "w" nor "c" are provided, else it + is ignored. + + - "t": enable tracing of SQL executed on this database handle, + sending it to `console.log()`. To disable it later, call + `sqlite3.capi.sqlite3_trace_v2(thisDb.pointer, 0, 0, 0)`. + + If "w" is not provided, the db is implicitly read-only, noting + that "rc" is meaningless + + Any other letters are currently ignored. The default is + "c". These modes are ignored for the special ":memory:" and "" + names and _may_ be ignored altogether for certain VFSes. + + The final argument is analogous to the final argument of + sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value, + or none at all, to use the default. If passed a value, it must + be the string name of a VFS. + + The constructor optionally (and preferably) takes its arguments + in the form of a single configuration object with the following + properties: + + - `filename`: database file name + - `flags`: open-mode flags + - `vfs`: the VFS fname + + + The `filename` and `vfs` arguments may be either JS strings or + C-strings allocated via WASM. `flags` is required to be a JS + string (because it's specific to this API, which is specific + to JS). + + For purposes of passing a DB instance to C-style sqlite3 + functions, the DB object's read-only `pointer` property holds its + `sqlite3*` pointer value. That property can also be used to check + whether this DB instance is still open: it will evaluate to + `undefined` after the DB object's close() method is called. + + In the main window thread, the filenames `":localStorage:"` and + `":sessionStorage:"` are special: they cause the db to use either + localStorage or sessionStorage for storing the database using + the kvvfs. If one of these names are used, they trump + any vfs name set in the arguments. + */ + const DB = function(...args) { + dbCtorHelper.apply(this, args); + }; + DB.dbCtorHelper = dbCtorHelper; + /** + Internal-use enum for mapping JS types to DB-bindable types. + These do not (and need not) line up with the SQLITE_type + values. All values in this enum must be truthy and (mostly) + distinct but they need not be numbers. + */ + const BindTypes = { + null: 1, + number: 2, + string: 3, + boolean: 4, + blob: 5 + }; + if (wasm.bigIntEnabled) BindTypes.bigint = BindTypes.number; + /** + This class wraps sqlite3_stmt. Calling this constructor + directly will trigger an exception. Use DB.prepare() to create + new instances. + + For purposes of passing a Stmt instance to C-style sqlite3 + functions, its read-only `pointer` property holds its `sqlite3_stmt*` + pointer value. + + Other non-function properties include: + + - `db`: the DB object which created the statement. + + - `columnCount`: the number of result columns in the query, or 0 + for queries which cannot return results. This property is a + read-only proxy for sqlite3_column_count() and its use in loops + should be avoided because of the call overhead associated with + that. The `columnCount` is not cached when the Stmt is created + because a schema change made between this statement's preparation + and when it is stepped may invalidate it. + + - `parameterCount`: the number of bindable parameters in the + query. Like `columnCount`, this property is ready-only and is a + proxy for a C API call. + + As a general rule, most methods of this class will throw if + called on an instance which has been finalized. For brevity's + sake, the method docs do not all repeat this warning. + */ + const Stmt = function() { + if (BindTypes !== arguments[2]) toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare()."); + this.db = arguments[0]; + __ptrMap.set(this, arguments[1]); + if (arguments.length > 3 && !arguments[3]) __doesNotOwnHandle.add(this); + }; + /** Throws if the given DB has been closed, else it is returned. */ + const affirmDbOpen = function(db) { + if (!db.pointer) toss3("DB has been closed."); + return db; + }; + /** Throws if ndx is not an integer or if it is out of range + for stmt.columnCount, else returns stmt. + + Reminder: this will also fail after the statement is finalized + but the resulting error will be about an out-of-bounds column + index rather than a statement-is-finalized error. + */ + const affirmColIndex = function(stmt, ndx) { + if (ndx !== (ndx | 0) || ndx < 0 || ndx >= stmt.columnCount) toss3("Column index", ndx, "is out of range."); + return stmt; + }; + /** + Expects to be passed the `arguments` object from DB.exec(). Does + the argument processing/validation, throws on error, and returns + a new object on success: + + { sql: the SQL, opt: optionsObj, cbArg: function} + + The opt object is a normalized copy of any passed to this + function. The sql will be converted to a string if it is provided + in one of the supported non-string formats. + + cbArg is only set if the opt.callback or opt.resultRows are set, + in which case it's a function which expects to be passed the + current Stmt and returns the callback argument of the type + indicated by the input arguments. + */ + const parseExecArgs = function(db, args) { + const out = Object.create(null); + out.opt = Object.create(null); + switch (args.length) { + case 1: + if ("string" === typeof args[0] || util.isSQLableTypedArray(args[0])) out.sql = args[0]; + else if (Array.isArray(args[0])) out.sql = args[0]; + else if (args[0] && "object" === typeof args[0]) { + out.opt = args[0]; + out.sql = out.opt.sql; + } + break; + case 2: + out.sql = args[0]; + out.opt = args[1]; + break; + default: toss3("Invalid argument count for exec()."); + } + out.sql = util.flexibleString(out.sql); + if ("string" !== typeof out.sql) toss3("Missing SQL argument or unsupported SQL value type."); + const opt = out.opt; + switch (opt.returnValue) { + case "resultRows": + if (!opt.resultRows) opt.resultRows = []; + out.returnVal = () => opt.resultRows; + break; + case "saveSql": + if (!opt.saveSql) opt.saveSql = []; + out.returnVal = () => opt.saveSql; + break; + case void 0: + case "this": + out.returnVal = () => db; + break; + default: toss3("Invalid returnValue value:", opt.returnValue); + } + if (!opt.callback && !opt.returnValue && void 0 !== opt.rowMode) { + if (!opt.resultRows) opt.resultRows = []; + out.returnVal = () => opt.resultRows; + } + if (opt.callback || opt.resultRows) switch (void 0 === opt.rowMode ? "array" : opt.rowMode) { + case "object": + out.cbArg = (stmt, cache) => { + if (!cache.columnNames) cache.columnNames = stmt.getColumnNames([]); + const row = stmt.get([]); + const rv = Object.create(null); + for (const i in cache.columnNames) rv[cache.columnNames[i]] = row[i]; + return rv; + }; + break; + case "array": + out.cbArg = (stmt) => stmt.get([]); + break; + case "stmt": + if (Array.isArray(opt.resultRows)) toss3("exec(): invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", "a result column number, or column name reference."); + out.cbArg = (stmt) => stmt; + break; + default: + if (util.isInt32(opt.rowMode)) { + out.cbArg = (stmt) => stmt.get(opt.rowMode); + break; + } else if ("string" === typeof opt.rowMode && opt.rowMode.length > 1 && "$" === opt.rowMode[0]) { + const $colName = opt.rowMode.substr(1); + out.cbArg = (stmt) => { + const rc = stmt.get(Object.create(null))[$colName]; + return void 0 === rc ? toss3(capi.SQLITE_NOTFOUND, "exec(): unknown result column:", $colName) : rc; + }; + break; + } + toss3("Invalid rowMode:", opt.rowMode); + } + return out; + }; + /** + Internal impl of the DB.selectValue(), selectArray(), and + selectObject() methods. + */ + const __selectFirstRow = (db, sql, bind, ...getArgs) => { + const stmt = db.prepare(sql); + try { + const rc = stmt.bind(bind).step() ? stmt.get(...getArgs) : void 0; + stmt.reset(); + return rc; + } finally { + stmt.finalize(); + } + }; + /** + Internal impl of the DB.selectArrays() and selectObjects() + methods. + */ + const __selectAll = (db, sql, bind, rowMode) => db.exec({ + sql, + bind, + rowMode, + returnValue: "resultRows" + }); + /** + Expects to be given a DB instance or an `sqlite3*` pointer (may + be null) and an sqlite3 API result code. If the result code is + not falsy, this function throws an SQLite3Error with an error + message from sqlite3_errmsg(), using db (or, if db is-a DB, + db.pointer) as the db handle, or sqlite3_errstr() if db is + falsy. Note that if it's passed a non-error code like SQLITE_ROW + or SQLITE_DONE, it will still throw but the error string might be + "Not an error." The various non-0 non-error codes need to be + checked for in client code where they are expected. + + The thrown exception's `resultCode` property will be the value of + the second argument to this function. + + If it does not throw, it returns its first argument. + */ + DB.checkRc = (db, resultCode) => checkSqlite3Rc(db, resultCode); + DB.prototype = { + isOpen: function() { + return !!this.pointer; + }, + affirmOpen: function() { + return affirmDbOpen(this); + }, + close: function() { + const pDb = this.pointer; + if (pDb) { + if (this.onclose && this.onclose.before instanceof Function) try { + this.onclose.before(this); + } catch (e) {} + Object.keys(__stmtMap.get(this)).forEach((k, s) => { + if (s && s.pointer) try { + s.finalize(); + } catch (e) {} + }); + __ptrMap.delete(this); + __stmtMap.delete(this); + if (!__doesNotOwnHandle.delete(this)) capi.sqlite3_close_v2(pDb); + if (this.onclose && this.onclose.after instanceof Function) try { + this.onclose.after(this); + } catch (e) {} + delete this.filename; + } + }, + changes: function(total = false, sixtyFour = false) { + const p = affirmDbOpen(this).pointer; + if (total) return sixtyFour ? capi.sqlite3_total_changes64(p) : capi.sqlite3_total_changes(p); + else return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p); + }, + dbFilename: function(dbName = "main") { + return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); + }, + dbName: function(dbNumber = 0) { + return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); + }, + dbVfsName: function(dbName = 0) { + let rc; + const pVfs = capi.sqlite3_js_db_vfs(affirmDbOpen(this).pointer, dbName); + if (pVfs) { + const v = new capi.sqlite3_vfs(pVfs); + try { + rc = wasm.cstrToJs(v.$zName); + } finally { + v.dispose(); + } + } + return rc; + }, + prepare: function(sql) { + affirmDbOpen(this); + const stack = wasm.pstack.pointer; + let ppStmt, pStmt; + try { + ppStmt = wasm.pstack.alloc(8); + DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); + pStmt = wasm.peekPtr(ppStmt); + } finally { + wasm.pstack.restore(stack); + } + if (!pStmt) toss3("Cannot prepare empty SQL."); + const stmt = new Stmt(this, pStmt, BindTypes); + __stmtMap.get(this)[pStmt] = stmt; + return stmt; + }, + exec: function() { + affirmDbOpen(this); + const arg = parseExecArgs(this, arguments); + if (!arg.sql) return toss3("exec() requires an SQL string."); + const opt = arg.opt; + const callback = opt.callback; + const resultRows = Array.isArray(opt.resultRows) ? opt.resultRows : void 0; + let stmt; + let bind = opt.bind; + let evalFirstResult = !!(arg.cbArg || opt.columnNames || resultRows); + const stack = wasm.scopedAllocPush(); + const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : void 0; + try { + const isTA = util.isSQLableTypedArray(arg.sql); + let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); + const ppStmt = wasm.scopedAlloc(2 * wasm.ptr.size + (sqlByteLen + 1)); + const pzTail = wasm.ptr.add(ppStmt, wasm.ptr.size); + let pSql = wasm.ptr.add(pzTail, wasm.ptr.size); + const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen); + if (isTA) wasm.heap8().set(arg.sql, pSql); + else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); + wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0); + while (pSql && wasm.peek8(pSql)) { + wasm.pokePtr([ppStmt, pzTail], 0); + DB.checkRc(this, capi.sqlite3_prepare_v3(this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail)); + const pStmt = wasm.peekPtr(ppStmt); + pSql = wasm.peekPtr(pzTail); + sqlByteLen = Number(wasm.ptr.add(pSqlEnd, -pSql)); + if (!pStmt) continue; + if (saveSql) saveSql.push(capi.sqlite3_sql(pStmt).trim()); + stmt = new Stmt(this, pStmt, BindTypes); + if (bind && stmt.parameterCount) { + stmt.bind(bind); + bind = null; + } + if (evalFirstResult && stmt.columnCount) { + let gotColNames = Array.isArray(opt.columnNames) ? 0 : 1; + evalFirstResult = false; + if (arg.cbArg || resultRows) { + const cbArgCache = Object.create(null); + for (; stmt.step(); __execLock.delete(stmt)) { + if (0 === gotColNames++) stmt.getColumnNames(cbArgCache.columnNames = opt.columnNames || []); + __execLock.add(stmt); + const row = arg.cbArg(stmt, cbArgCache); + if (resultRows) resultRows.push(row); + if (callback && false === callback.call(opt, row, stmt)) break; + } + __execLock.delete(stmt); + } + if (0 === gotColNames) stmt.getColumnNames(opt.columnNames); + } else stmt.step(); + stmt.reset().finalize(); + stmt = null; + } + } finally { + if (stmt) { + __execLock.delete(stmt); + stmt.finalize(); + } + wasm.scopedAllocPop(stack); + } + return arg.returnVal(); + }, + createFunction: function f(name, xFunc, opt) { + const isFunc = (f) => f instanceof Function; + switch (arguments.length) { + case 1: + opt = name; + name = opt.name; + xFunc = opt.xFunc || 0; + break; + case 2: + if (!isFunc(xFunc)) { + opt = xFunc; + xFunc = opt.xFunc || 0; + } + break; + case 3: break; + default: break; + } + if (!opt) opt = {}; + if ("string" !== typeof name) toss3("Invalid arguments: missing function name."); + let xStep = opt.xStep || 0; + let xFinal = opt.xFinal || 0; + const xValue = opt.xValue || 0; + const xInverse = opt.xInverse || 0; + let isWindow = void 0; + if (isFunc(xFunc)) { + isWindow = false; + if (isFunc(xStep) || isFunc(xFinal)) toss3("Ambiguous arguments: scalar or aggregate?"); + xStep = xFinal = null; + } else if (isFunc(xStep)) { + if (!isFunc(xFinal)) toss3("Missing xFinal() callback for aggregate or window UDF."); + xFunc = null; + } else if (isFunc(xFinal)) toss3("Missing xStep() callback for aggregate or window UDF."); + else toss3("Missing function-type properties."); + if (false === isWindow) { + if (isFunc(xValue) || isFunc(xInverse)) toss3("xValue and xInverse are not permitted for non-window UDFs."); + } else if (isFunc(xValue)) { + if (!isFunc(xInverse)) toss3("xInverse must be provided if xValue is."); + isWindow = true; + } else if (isFunc(xInverse)) toss3("xValue must be provided if xInverse is."); + const pApp = opt.pApp; + if (void 0 !== pApp && null !== pApp && !wasm.isPtr(pApp)) toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); + const xDestroy = opt.xDestroy || 0; + if (xDestroy && !isFunc(xDestroy)) toss3("xDestroy property must be a function."); + let fFlags = 0; + if (getOwnOption(opt, "deterministic")) fFlags |= capi.SQLITE_DETERMINISTIC; + if (getOwnOption(opt, "directOnly")) fFlags |= capi.SQLITE_DIRECTONLY; + if (getOwnOption(opt, "innocuous")) fFlags |= capi.SQLITE_INNOCUOUS; + name = name.toLowerCase(); + const xArity = xFunc || xStep; + const arity = getOwnOption(opt, "arity"); + const arityArg = "number" === typeof arity ? arity : xArity.length ? xArity.length - 1 : 0; + let rc; + if (isWindow) rc = capi.sqlite3_create_window_function(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xStep, xFinal, xValue, xInverse, xDestroy); + else rc = capi.sqlite3_create_function_v2(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xFunc, xStep, xFinal, xDestroy); + DB.checkRc(this, rc); + return this; + }, + selectValue: function(sql, bind, asType) { + return __selectFirstRow(this, sql, bind, 0, asType); + }, + selectValues: function(sql, bind, asType) { + const stmt = this.prepare(sql), rc = []; + try { + stmt.bind(bind); + while (stmt.step()) rc.push(stmt.get(0, asType)); + stmt.reset(); + } finally { + stmt.finalize(); + } + return rc; + }, + selectArray: function(sql, bind) { + return __selectFirstRow(this, sql, bind, []); + }, + selectObject: function(sql, bind) { + return __selectFirstRow(this, sql, bind, {}); + }, + selectArrays: function(sql, bind) { + return __selectAll(this, sql, bind, "array"); + }, + selectObjects: function(sql, bind) { + return __selectAll(this, sql, bind, "object"); + }, + openStatementCount: function() { + return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; + }, + transaction: function(callback) { + let opener = "BEGIN"; + if (arguments.length > 1) { + if (/[^a-zA-Z]/.test(arguments[0])) toss3(capi.SQLITE_MISUSE, "Invalid argument for BEGIN qualifier."); + opener += " " + arguments[0]; + callback = arguments[1]; + } + affirmDbOpen(this).exec(opener); + try { + const rc = callback(this); + this.exec("COMMIT"); + return rc; + } catch (e) { + this.exec("ROLLBACK"); + throw e; + } + }, + savepoint: function(callback) { + affirmDbOpen(this).exec("SAVEPOINT oo1"); + try { + const rc = callback(this); + this.exec("RELEASE oo1"); + return rc; + } catch (e) { + this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); + throw e; + } + }, + checkRc: function(resultCode) { + return checkSqlite3Rc(this, resultCode); + } + }; + /** + Returns a new oo1.DB instance which wraps the given (sqlite3*) + WASM pointer, optionally with or without taking over ownership of + that pointer. + + The first argument must be either a non-NULL (sqlite3*) WASM + pointer. + + The second argument, defaulting to false, specifies ownership of + the first argument. If it is truthy, the returned object will + pass that pointer to sqlite3_close() when its close() method is + called, otherwise it will not. + + Throws if pDb is not a non-0 WASM pointer. + + The caller MUST GUARANTEE that the passed-in handle will outlive + the returned object, i.e. that it will not be closed. If it is closed, + this object will hold a stale pointer and results are undefined. + + Aside from its lifetime, the proxy is to be treated as any other + DB instance, including the requirement of calling close() on + it. close() will free up internal resources owned by the proxy + and disassociate the proxy from that handle but will not + actually close the proxied db handle unless this function is + passed a thruthy second argument. + + To stress: + + - DO NOT call sqlite3_close() (or similar) on the being-proxied + pointer while a proxy is active. + + - ALWAYS eventually call close() on the returned object. If the + proxy does not own the underlying handle then its MUST be + closed BEFORE the being-proxied handle is closed. + + Design notes: + + - wrapHandle() "could" accept a DB object instance as its first + argument and proxy thatDb.pointer but there is currently no use + case where doing so would be useful, so it does not allow + that. That restriction may be lifted in a future version. + */ + DB.wrapHandle = function(pDb, takeOwnership = false) { + if (!pDb || !wasm.isPtr(pDb)) throw new sqlite3.SQLite3Error(capi.SQLITE_MISUSE, "Argument must be a WASM sqlite3 pointer"); + return new DB({ + "sqlite3*": pDb, + "sqlite3*:takeOwnership": !!takeOwnership + }); + }; + /** Throws if the given Stmt has been finalized, else stmt is + returned. */ + const affirmStmtOpen = function(stmt) { + if (!stmt.pointer) toss3("Stmt has been closed."); + return stmt; + }; + /** Returns an opaque truthy value from the BindTypes + enum if v's type is a valid bindable type, else + returns a falsy value. As a special case, a value of + undefined is treated as a bind type of null. */ + const isSupportedBindType = function(v) { + let t = BindTypes[null === v || void 0 === v ? "null" : typeof v]; + switch (t) { + case BindTypes.boolean: + case BindTypes.null: + case BindTypes.number: + case BindTypes.string: return t; + case BindTypes.bigint: return wasm.bigIntEnabled ? t : void 0; + default: return util.isBindableTypedArray(v) ? BindTypes.blob : void 0; + } + }; + /** + If isSupportedBindType(v) returns a truthy value, this + function returns that value, else it throws. + */ + const affirmSupportedBindType = function(v) { + return isSupportedBindType(v) || toss3("Unsupported bind() argument type:", typeof v); + }; + /** + If key is a number and within range of stmt's bound parameter + count, key is returned. + + If key is not a number then it must be a JS string (not a WASM + string) and it is checked against named parameters. If a match is + found, its index is returned. + + Else it throws. + */ + const affirmParamIndex = function(stmt, key) { + const n = "number" === typeof key ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); + if (0 === n || !util.isInt32(n)) toss3("Invalid bind() parameter name: " + key); + else if (n < 1 || n > stmt.parameterCount) toss3("Bind index", key, "is out of range."); + return n; + }; + /** + Each Stmt object which is "locked" by DB.exec() gets an entry + here to note that "lock". + + The reason this is in place is because exec({callback:...})'s + callback gets access to the Stmt objects created internally by + exec() but it must not use certain Stmt APIs. + */ + const __execLock = /* @__PURE__ */ new Set(); + /** + This is a Stmt.get() counterpart of __execLock. Each time + Stmt.step() returns true, the statement is added to this set, + indicating that Stmt.get() is legal. Stmt APIs which invalidate + that status remove the Stmt object from this set, which will + cause Stmt.get() to throw with a descriptive error message + instead of a more generic "API misuse" if we were to allow that + call to reach the C API. + */ + const __stmtMayGet = /* @__PURE__ */ new Set(); + /** + Stmt APIs which are prohibited on locked objects must call + affirmNotLockedByExec() before doing any work. + + If __execLock.has(stmt) is truthy, this throws an exception + complaining that the 2nd argument (an operation name, + e.g. "bind()") is not legal while the statement is "locked". + Locking happens before an exec()-like callback is passed a + statement, to ensure that the callback does not mutate or + finalize the statement. If it does not throw, it returns stmt. + */ + const affirmNotLockedByExec = function(stmt, currentOpName) { + if (__execLock.has(stmt)) toss3("Operation is illegal when statement is locked:", currentOpName); + return stmt; + }; + /** + Binds a single bound parameter value on the given stmt at the + given index (numeric or named) using the given bindType (see + the BindTypes enum) and value. Throws on error. Returns stmt on + success. + */ + const bindOne = function f(stmt, ndx, bindType, val) { + affirmNotLockedByExec(affirmStmtOpen(stmt), "bind()"); + if (!f._) { + f._tooBigInt = (v) => toss3("BigInt value is too big to store without precision loss:", v); + f._ = { string: function(stmt, ndx, val, asBlob) { + const [pStr, n] = wasm.allocCString(val, true); + return (asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text)(stmt.pointer, ndx, pStr, n, capi.SQLITE_WASM_DEALLOC); + } }; + } + affirmSupportedBindType(val); + ndx = affirmParamIndex(stmt, ndx); + let rc = 0; + switch (null === val || void 0 === val ? BindTypes.null : bindType) { + case BindTypes.null: + rc = capi.sqlite3_bind_null(stmt.pointer, ndx); + break; + case BindTypes.string: + rc = f._.string(stmt, ndx, val, false); + break; + case BindTypes.number: { + let m; + if (util.isInt32(val)) m = capi.sqlite3_bind_int; + else if ("bigint" === typeof val) if (!util.bigIntFits64(val)) f._tooBigInt(val); + else if (wasm.bigIntEnabled) m = capi.sqlite3_bind_int64; + else if (util.bigIntFitsDouble(val)) { + val = Number(val); + m = capi.sqlite3_bind_double; + } else f._tooBigInt(val); + else { + val = Number(val); + if (wasm.bigIntEnabled && Number.isInteger(val)) m = capi.sqlite3_bind_int64; + else m = capi.sqlite3_bind_double; + } + rc = m(stmt.pointer, ndx, val); + break; + } + case BindTypes.boolean: + rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); + break; + case BindTypes.blob: { + if ("string" === typeof val) { + rc = f._.string(stmt, ndx, val, true); + break; + } else if (val instanceof ArrayBuffer) val = new Uint8Array(val); + else if (!util.isBindableTypedArray(val)) toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, Int8Array, or ArrayBuffer."); + const pBlob = wasm.alloc(val.byteLength || 1); + wasm.heap8().set(val.byteLength ? val : [0], Number(pBlob)); + rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); + break; + } + default: + sqlite3.config.warn("Unsupported bind() argument type:", val); + toss3("Unsupported bind() argument type: " + typeof val); + } + if (rc) DB.checkRc(stmt.db.pointer, rc); + return stmt; + }; + Stmt.prototype = { + finalize: function() { + const ptr = this.pointer; + if (ptr) { + affirmNotLockedByExec(this, "finalize()"); + const rc = __doesNotOwnHandle.delete(this) ? 0 : capi.sqlite3_finalize(ptr); + delete __stmtMap.get(this.db)[ptr]; + __ptrMap.delete(this); + __execLock.delete(this); + __stmtMayGet.delete(this); + delete this.parameterCount; + delete this.db; + return rc; + } + }, + clearBindings: function() { + affirmNotLockedByExec(affirmStmtOpen(this), "clearBindings()"); + capi.sqlite3_clear_bindings(this.pointer); + __stmtMayGet.delete(this); + return this; + }, + reset: function(alsoClearBinds) { + affirmNotLockedByExec(this, "reset()"); + if (alsoClearBinds) this.clearBindings(); + const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer); + __stmtMayGet.delete(this); + checkSqlite3Rc(this.db, rc); + return this; + }, + bind: function() { + affirmStmtOpen(this); + let ndx, arg; + switch (arguments.length) { + case 1: + ndx = 1; + arg = arguments[0]; + break; + case 2: + ndx = arguments[0]; + arg = arguments[1]; + break; + default: toss3("Invalid bind() arguments."); + } + if (void 0 === arg) return this; + else if (!this.parameterCount) toss3("This statement has no bindable parameters."); + __stmtMayGet.delete(this); + if (null === arg) return bindOne(this, ndx, BindTypes.null, arg); + else if (Array.isArray(arg)) { + if (1 !== arguments.length) toss3("When binding an array, an index argument is not permitted."); + arg.forEach((v, i) => bindOne(this, i + 1, affirmSupportedBindType(v), v)); + return this; + } else if (arg instanceof ArrayBuffer) arg = new Uint8Array(arg); + if ("object" === typeof arg && !util.isBindableTypedArray(arg)) { + if (1 !== arguments.length) toss3("When binding an object, an index argument is not permitted."); + Object.keys(arg).forEach((k) => bindOne(this, k, affirmSupportedBindType(arg[k]), arg[k])); + return this; + } else return bindOne(this, ndx, affirmSupportedBindType(arg), arg); + toss3("Should not reach this point."); + }, + bindAsBlob: function(ndx, arg) { + affirmStmtOpen(this); + if (1 === arguments.length) { + arg = ndx; + ndx = 1; + } + const t = affirmSupportedBindType(arg); + if (BindTypes.string !== t && BindTypes.blob !== t && BindTypes.null !== t) toss3("Invalid value type for bindAsBlob()"); + return bindOne(this, ndx, BindTypes.blob, arg); + }, + step: function() { + affirmNotLockedByExec(this, "step()"); + const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); + switch (rc) { + case capi.SQLITE_DONE: + __stmtMayGet.delete(this); + return false; + case capi.SQLITE_ROW: + __stmtMayGet.add(this); + return true; + default: + __stmtMayGet.delete(this); + sqlite3.config.warn("sqlite3_step() rc=", rc, capi.sqlite3_js_rc_str(rc), "SQL =", capi.sqlite3_sql(this.pointer)); + DB.checkRc(this.db.pointer, rc); + } + }, + stepReset: function() { + this.step(); + return this.reset(); + }, + stepFinalize: function() { + try { + const rc = this.step(); + this.reset(); + return rc; + } finally { + try { + this.finalize(); + } catch (e) {} + } + }, + get: function(ndx, asType) { + if (!__stmtMayGet.has(affirmStmtOpen(this))) toss3("Stmt.step() has not (recently) returned true."); + if (Array.isArray(ndx)) { + let i = 0; + const n = this.columnCount; + while (i < n) ndx[i] = this.get(i++); + return ndx; + } else if (ndx && "object" === typeof ndx) { + let i = 0; + const n = this.columnCount; + while (i < n) ndx[capi.sqlite3_column_name(this.pointer, i)] = this.get(i++); + return ndx; + } + affirmColIndex(this, ndx); + switch (void 0 === asType ? capi.sqlite3_column_type(this.pointer, ndx) : asType) { + case capi.SQLITE_NULL: return null; + case capi.SQLITE_INTEGER: if (wasm.bigIntEnabled) { + const rc = capi.sqlite3_column_int64(this.pointer, ndx); + if (rc >= Number.MIN_SAFE_INTEGER && rc <= Number.MAX_SAFE_INTEGER) return Number(rc).valueOf(); + return rc; + } else { + const rc = capi.sqlite3_column_double(this.pointer, ndx); + if (rc > Number.MAX_SAFE_INTEGER || rc < Number.MIN_SAFE_INTEGER) toss3("Integer is out of range for JS integer range: " + rc); + return util.isInt32(rc) ? rc | 0 : rc; + } + case capi.SQLITE_FLOAT: return capi.sqlite3_column_double(this.pointer, ndx); + case capi.SQLITE_TEXT: return capi.sqlite3_column_text(this.pointer, ndx); + case capi.SQLITE_BLOB: { + const n = capi.sqlite3_column_bytes(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx), rc = new Uint8Array(n); + if (n) { + rc.set(wasm.heap8u().slice(Number(ptr), Number(ptr) + n), 0); + if (this.db._blobXfer instanceof Array) this.db._blobXfer.push(rc.buffer); + } + return rc; + } + default: toss3("Don't know how to translate", "type of result column #" + ndx + "."); + } + toss3("Not reached."); + }, + getInt: function(ndx) { + return this.get(ndx, capi.SQLITE_INTEGER); + }, + getFloat: function(ndx) { + return this.get(ndx, capi.SQLITE_FLOAT); + }, + getString: function(ndx) { + return this.get(ndx, capi.SQLITE_TEXT); + }, + getBlob: function(ndx) { + return this.get(ndx, capi.SQLITE_BLOB); + }, + getJSON: function(ndx) { + const s = this.get(ndx, capi.SQLITE_STRING); + return null === s ? s : JSON.parse(s); + }, + getColumnName: function(ndx) { + return capi.sqlite3_column_name(affirmColIndex(affirmStmtOpen(this), ndx).pointer, ndx); + }, + getColumnNames: function(tgt = []) { + affirmColIndex(affirmStmtOpen(this), 0); + const n = this.columnCount; + for (let i = 0; i < n; ++i) tgt.push(capi.sqlite3_column_name(this.pointer, i)); + return tgt; + }, + getParamIndex: function(name) { + return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_index(this.pointer, name) : void 0; + }, + getParamName: function(ndx) { + return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_name(this.pointer, ndx) : void 0; + }, + isBusy: function() { + return 0 !== capi.sqlite3_stmt_busy(affirmStmtOpen(this)); + }, + isReadOnly: function() { + return 0 !== capi.sqlite3_stmt_readonly(affirmStmtOpen(this)); + } + }; + { + const prop = { + enumerable: true, + get: function() { + return __ptrMap.get(this); + }, + set: () => toss3("The pointer property is read-only.") + }; + Object.defineProperty(Stmt.prototype, "pointer", prop); + Object.defineProperty(DB.prototype, "pointer", prop); + } + /** + Stmt.columnCount is an interceptor for sqlite3_column_count(). + + This requires an unfortunate performance hit compared to caching + columnCount when the Stmt is created/prepared (as was done in + SQLite <=3.42.0), but is necessary in order to handle certain + corner cases, as described in + https://sqlite.org/forum/forumpost/7774b773937cbe0a. + */ + Object.defineProperty(Stmt.prototype, "columnCount", { + enumerable: false, + get: function() { + return capi.sqlite3_column_count(this.pointer); + }, + set: () => toss3("The columnCount property is read-only.") + }); + Object.defineProperty(Stmt.prototype, "parameterCount", { + enumerable: false, + get: function() { + return capi.sqlite3_bind_parameter_count(this.pointer); + }, + set: () => toss3("The parameterCount property is read-only.") + }); + /** + The Stmt counterpart of oo1.DB.wrapHandle(), this creates a Stmt + instance which wraps a WASM (sqlite3_stmt*) in the oo1 API, + optionally with or without taking over ownership of that pointer. + + The first argument must be an oo1.DB instance[^1]. + + The second argument must be a valid WASM (sqlite3_stmt*), as + produced by sqlite3_prepare_v2() and sqlite3_prepare_v3(). + + The third argument, defaulting to false, specifies whether the + returned Stmt object takes over ownership of the underlying + (sqlite3_stmt*). If true, the returned object's finalize() method + will finalize that handle, else it will not. If it is false, + ownership of pStmt is unchanged and pStmt MUST outlive the + returned object or results are undefined. + + This function throws if the arguments are invalid. On success it + returns a new Stmt object which wraps the given statement + pointer. + + Like all Stmt objects, the finalize() method must eventually be + called on the returned object to free up internal resources, + regardless of whether this function's third argument is true or + not. + + [^1]: The first argument cannot be a (sqlite3*) because the + resulting Stmt object requires a parent DB object. It is not yet + determined whether it would be of general benefit to refactor the + DB/Stmt pair internals to communicate in terms of the underlying + (sqlite3*) rather than a DB object. If so, we could laxen the + first argument's requirement and allow an (sqlite3*). Because + DB.wrapHandle() enables multiple DB objects to proxy the same + (sqlite3*), we cannot unambiguously translate the first arugment + from (sqlite3*) to DB instances for us with this function's first + argument. + */ + Stmt.wrapHandle = function(oo1db, pStmt, takeOwnership = false) { + if (!(oo1db instanceof DB) || !oo1db.pointer) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "First argument must be an opened sqlite3.oo1.DB instance"); + if (!pStmt || !wasm.isPtr(pStmt)) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "Second argument must be a WASM sqlite3_stmt pointer"); + return new Stmt(oo1db, pStmt, BindTypes, !!takeOwnership); + }; + /** The OO API's public namespace. */ + sqlite3.oo1 = { + DB, + Stmt + }; + }); + /** + 2022-07-22 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This file implements the initializer for SQLite's "Worker API #1", a + very basic DB access API intended to be scripted from a main window + thread via Worker-style messages. Because of limitations in that + type of communication, this API is minimalistic and only capable of + serving relatively basic DB requests (e.g. it cannot process nested + query loops concurrently). + + This file requires that the core C-style sqlite3 API and OO API #1 + have been loaded. + */ + /** + sqlite3.initWorker1API() implements a Worker-based wrapper around + SQLite3 OO API #1, colloquially known as "Worker API #1". + + In order to permit this API to be loaded in worker threads without + automatically registering onmessage handlers, initializing the + worker API requires calling initWorker1API(). If this function is + called from a non-worker thread then it throws an exception. It + must only be called once per Worker. + + When initialized, it installs message listeners to receive Worker + messages and then it posts a message in the form: + + ``` + {type:'sqlite3-api', result:'worker1-ready'} + ``` + + to let the client know that it has been initialized. Clients may + optionally depend on this function not returning until + initialization is complete, as the initialization is synchronous. + In some contexts, however, listening for the above message is + a better fit. + + Note that the worker-based interface can be slightly quirky because + of its async nature. In particular, any number of messages may be posted + to the worker before it starts handling any of them. If, e.g., an + "open" operation fails, any subsequent messages will fail. The + Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`) + is more comfortable to use in that regard. + + The documentation for the input and output worker messages for + this API follows... + + ==================================================================== + Common message format... + + Each message posted to the worker has an operation-independent + envelope and operation-dependent arguments: + + ``` + { + type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get' + + messageId: OPTIONAL arbitrary value. The worker will copy it as-is + into response messages to assist in client-side dispatching. + + dbId: a db identifier string (returned by 'open') which tells the + operation which database instance to work on. If not provided, the + first-opened db is used. This is an "opaque" value, with no + inherently useful syntax or information. Its value is subject to + change with any given build of this API and cannot be used as a + basis for anything useful beyond its one intended purpose. + + args: ...operation-dependent arguments... + + // the framework may add other properties for testing or debugging + // purposes. + + } + ``` + + Response messages, posted back to the main thread, look like: + + ``` + { + type: string. Same as above except for error responses, which have the type + 'error', + + messageId: same value, if any, provided by the inbound message + + dbId: the id of the db which was operated on, if any, as returned + by the corresponding 'open' operation. + + result: ...operation-dependent result... + + } + ``` + + ==================================================================== + Error responses + + Errors are reported messages in an operation-independent format: + + ``` + { + type: "error", + + messageId: ...as above..., + + dbId: ...as above... + + result: { + + operation: type of the triggering operation: 'open', 'close', ... + + message: ...error message text... + + errorClass: string. The ErrorClass.name property from the thrown exception. + + input: the message object which triggered the error. + + stack: _if available_, a stack trace array. + + } + + } + ``` + + + ==================================================================== + "config-get" + + This operation fetches the serializable parts of the sqlite3 API + configuration. + + Message format: + + ``` + { + type: "config-get", + messageId: ...as above..., + args: currently ignored and may be elided. + } + ``` + + Response: + + ``` + { + type: "config-get", + messageId: ...as above..., + result: { + + version: sqlite3.version object + + bigIntEnabled: bool. True if BigInt support is enabled. + + vfsList: result of sqlite3.capi.sqlite3_js_vfs_list() + } + } + ``` + + + ==================================================================== + "open" a database + + Message format: + + ``` + { + type: "open", + messageId: ...as above..., + args:{ + + filename [=":memory:" or "" (unspecified)]: the db filename. + See the sqlite3.oo1.DB constructor for peculiarities and + transformations, + + vfs: sqlite3_vfs name. Ignored if filename is ":memory:" or "". + This may change how the given filename is resolved. + } + } + ``` + + Response: + + ``` + { + type: "open", + messageId: ...as above..., + result: { + filename: db filename, possibly differing from the input. + + dbId: an opaque ID value which must be passed in the message + envelope to other calls in this API to tell them which db to + use. If it is not provided to future calls, they will default to + operating on the least-recently-opened db. This property is, for + API consistency's sake, also part of the containing message + envelope. Only the `open` operation includes it in the `result` + property. + + persistent: true if the given filename resides in the + known-persistent storage, else false. + + vfs: name of the VFS the "main" db is using. + } + } + ``` + + ==================================================================== + "close" a database + + Message format: + + ``` + { + type: "close", + messageId: ...as above... + dbId: ...as above... + args: OPTIONAL {unlink: boolean} + } + ``` + + If the `dbId` does not refer to an opened ID, this is a no-op. If + the `args` object contains a truthy `unlink` value then the database + will be unlinked (deleted) after closing it. The inability to close a + db (because it's not opened) or delete its file does not trigger an + error. + + Response: + + ``` + { + type: "close", + messageId: ...as above..., + result: { + + filename: filename of closed db, or undefined if no db was closed + + } + } + ``` + + ==================================================================== + "exec" SQL + + All SQL execution is processed through the exec operation. It offers + most of the features of the oo1.DB.exec() method, with a few limitations + imposed by the state having to cross thread boundaries. + + Message format: + + ``` + { + type: "exec", + messageId: ...as above... + dbId: ...as above... + args: string (SQL) or {... see below ...} + } + ``` + + Response: + + ``` + { + type: "exec", + messageId: ...as above..., + dbId: ...as above... + result: { + input arguments, possibly modified. See below. + } + } + ``` + + The arguments are in the same form accepted by oo1.DB.exec(), with + the exceptions noted below. + + If `args.countChanges` (added in version 3.43) is truthy then the + `result` property contained by the returned object will have a + `changeCount` property which holds the number of changes made by the + provided SQL. Because the SQL may contain an arbitrary number of + statements, the `changeCount` is calculated by calling + `sqlite3_total_changes()` before and after the SQL is evaluated. If + the value of `countChanges` is 64 then the `changeCount` property + will be returned as a 64-bit integer in the form of a BigInt (noting + that that will trigger an exception if used in a BigInt-incapable + build). In the latter case, the number of changes is calculated by + calling `sqlite3_total_changes64()` before and after the SQL is + evaluated. + + If the `args.lastInsertRowId` (added in version 3.50.0) is truthy + then the `result` property contained by the returned object will + have a `lastInsertRowId` will hold a BigInt-type value corresponding + to the result of sqlite3_last_insert_rowid(). This value is only + fetched once, after the SQL is run, regardless of how many + statements the SQL contains. This API has no idea whether the SQL + contains any INSERTs, so it is up to the client to apply/rely on + this property only when it makes sense to do so. + + A function-type args.callback property cannot cross + the window/Worker boundary, so is not useful here. If + args.callback is a string then it is assumed to be a + message type key, in which case a callback function will be + applied which posts each row result via: + + postMessage({type: thatKeyType, + rowNumber: 1-based-#, + row: theRow, + columnNames: anArray + }) + + And, at the end of the result set (whether or not any result rows + were produced), it will post an identical message with + (row=undefined, rowNumber=null) to alert the caller than the result + set is completed. Note that a row value of `null` is a legal row + result for certain arg.rowMode values. + + (Design note: we don't use (row=undefined, rowNumber=undefined) to + indicate end-of-results because fetching those would be + indistinguishable from fetching from an empty object unless the + client used hasOwnProperty() (or similar) to distinguish "missing + property" from "property with the undefined value". Similarly, + `null` is a legal value for `row` in some case , whereas the db + layer won't emit a result value of `undefined`.) + + The callback proxy must not recurse into this interface. An exec() + call will tie up the Worker thread, causing any recursion attempt + to wait until the first exec() is completed. + + The response is the input options object (or a synthesized one if + passed only a string), noting that options.resultRows and + options.columnNames may be populated by the call to db.exec(). + + + ==================================================================== + "export" the current db + + To export the underlying database as a byte array... + + Message format: + + ``` + { + type: "export", + messageId: ...as above..., + dbId: ...as above... + } + ``` + + Response: + + ``` + { + type: "export", + messageId: ...as above..., + dbId: ...as above... + result: { + byteArray: Uint8Array (as per sqlite3_js_db_export()), + filename: the db filename, + mimetype: "application/x-sqlite3" + } + } + ``` + + */ + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + const util = sqlite3.util; + sqlite3.initWorker1API = function() { + "use strict"; + const toss = (...args) => { + throw new Error(args.join(" ")); + }; + if (!(globalThis.WorkerGlobalScope instanceof Function)) toss("initWorker1API() must be run from a Worker thread."); + const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); + const DB = sqlite3.oo1.DB; + /** + Returns the app-wide unique ID for the given db, creating one if + needed. + */ + const getDbId = function(db) { + let id = wState.idMap.get(db); + if (id) return id; + id = "db#" + ++wState.idSeq + ":" + Math.floor(Math.random() * 1e8) + ":" + Math.floor(Math.random() * 1e8); + /** ^^^ can't simply use db.pointer b/c closing/opening may re-use + the same address, which could map pending messages to a wrong + instance. + + 2025-07: https://github.com/sqlite/sqlite-wasm/issues/113 + demonstrates that two Worker1s can end up with the same IDs, + despite using different instances of the library, so we need + to add some randomness to the IDs instead of relying on the + pointer addresses. + */ + wState.idMap.set(db, id); + return id; + }; + /** + Internal helper for managing Worker-level state. + */ + const wState = { + dbList: [], + idSeq: 0, + idMap: /* @__PURE__ */ new WeakMap(), + xfer: [], + open: function(opt) { + const db = new DB(opt); + this.dbs[getDbId(db)] = db; + if (this.dbList.indexOf(db) < 0) this.dbList.push(db); + return db; + }, + close: function(db, alsoUnlink) { + if (db) { + delete this.dbs[getDbId(db)]; + const filename = db.filename; + const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0); + db.close(); + const ddNdx = this.dbList.indexOf(db); + if (ddNdx >= 0) this.dbList.splice(ddNdx, 1); + if (alsoUnlink && filename && pVfs) util.sqlite3__wasm_vfs_unlink(pVfs, filename); + } + }, + post: function(msg, xferList) { + if (xferList && xferList.length) { + globalThis.postMessage(msg, Array.from(xferList)); + xferList.length = 0; + } else globalThis.postMessage(msg); + }, + dbs: Object.create(null), + getDb: function(id, require = true) { + return this.dbs[id] || (require ? toss("Unknown (or closed) DB ID:", id) : void 0); + } + }; + /** Throws if the given db is falsy or not opened, else returns its + argument. */ + const affirmDbOpen = function(db = wState.dbList[0]) { + return db && db.pointer ? db : toss("DB is not opened."); + }; + /** Extract dbId from the given message payload. */ + const getMsgDb = function(msgData, affirmExists = true) { + const db = wState.getDb(msgData.dbId, false) || wState.dbList[0]; + return affirmExists ? affirmDbOpen(db) : db; + }; + const getDefaultDbId = function() { + return wState.dbList[0] && getDbId(wState.dbList[0]); + }; + /** + A level of "organizational abstraction" for the Worker1 + API. Each method in this object must map directly to a Worker1 + message type key. The onmessage() dispatcher attempts to + dispatch all inbound messages to a method of this object, + passing it the event.data part of the inbound event object. All + methods must return a plain Object containing any result + state, which the dispatcher may amend. All methods must throw + on error. + */ + const wMsgHandler = { + open: function(ev) { + const oargs = Object.create(null), args = ev.args || Object.create(null); + if (args.simulateError) toss("Throwing because of simulateError flag."); + const rc = Object.create(null); + oargs.vfs = args.vfs; + oargs.filename = args.filename || ""; + const db = wState.open(oargs); + rc.filename = db.filename; + rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); + rc.dbId = getDbId(db); + rc.vfs = db.dbVfsName(); + return rc; + }, + close: function(ev) { + const db = getMsgDb(ev, false); + const response = { filename: db && db.filename }; + if (db) { + const doUnlink = ev.args && "object" === typeof ev.args ? !!ev.args.unlink : false; + wState.close(db, doUnlink); + } + return response; + }, + exec: function(ev) { + const rc = "string" === typeof ev.args ? { sql: ev.args } : ev.args || Object.create(null); + if ("stmt" === rc.rowMode) toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); + else if (!rc.sql) toss("'exec' requires input SQL."); + const db = getMsgDb(ev); + if (rc.callback || Array.isArray(rc.resultRows)) db._blobXfer = wState.xfer; + const theCallback = rc.callback; + let rowNumber = 0; + const hadColNames = !!rc.columnNames; + if ("string" === typeof theCallback) { + if (!hadColNames) rc.columnNames = []; + rc.callback = function(row, stmt) { + wState.post({ + type: theCallback, + columnNames: rc.columnNames, + rowNumber: ++rowNumber, + row + }, wState.xfer); + }; + } + try { + const changeCount = !!rc.countChanges ? db.changes(true, 64 === rc.countChanges) : void 0; + db.exec(rc); + if (void 0 !== changeCount) rc.changeCount = db.changes(true, 64 === rc.countChanges) - changeCount; + const lastInsertRowId = !!rc.lastInsertRowId ? sqlite3.capi.sqlite3_last_insert_rowid(db) : void 0; + if (void 0 !== lastInsertRowId) rc.lastInsertRowId = lastInsertRowId; + if (rc.callback instanceof Function) { + rc.callback = theCallback; + wState.post({ + type: theCallback, + columnNames: rc.columnNames, + rowNumber: null, + row: void 0 + }); + } + } finally { + delete db._blobXfer; + if (rc.callback) rc.callback = theCallback; + } + return rc; + }, + "config-get": function() { + const rc = Object.create(null), src = sqlite3.config; + ["bigIntEnabled"].forEach(function(k) { + if (Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; + }); + rc.version = sqlite3.version; + rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list(); + return rc; + }, + export: function(ev) { + const db = getMsgDb(ev); + const response = { + byteArray: sqlite3.capi.sqlite3_js_db_export(db.pointer), + filename: db.filename, + mimetype: "application/x-sqlite3" + }; + wState.xfer.push(response.byteArray.buffer); + return response; + }, + toss: function(ev) { + toss("Testing worker exception"); + } + }; + globalThis.onmessage = async function(ev) { + ev = ev.data; + let result, dbId = ev.dbId, evType = ev.type; + const arrivalTime = performance.now(); + try { + if (wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function) result = await wMsgHandler[evType](ev); + else toss("Unknown db worker message type:", ev.type); + } catch (err) { + evType = "error"; + result = { + operation: ev.type, + message: err.message, + errorClass: err.name, + input: ev + }; + if (err.stack) result.stack = "string" === typeof err.stack ? err.stack.split(/\n\s*/) : err.stack; + } + if (!dbId) dbId = result.dbId || getDefaultDbId(); + wState.post({ + type: evType, + dbId, + messageId: ev.messageId, + workerReceivedTime: arrivalTime, + workerRespondTime: performance.now(), + departureTime: ev.departureTime, + result + }, wState.xfer); + }; + globalThis.postMessage({ + type: "sqlite3-api", + result: "worker1-ready" + }); + }.bind({ sqlite3 }); + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; + const vfs = Object.create(null); + sqlite3.vfs = vfs; + /** + Uses sqlite3_vfs_register() to register this + sqlite3.capi.sqlite3_vfs instance. This object must have already + been filled out properly. If the first argument is truthy, the + VFS is registered as the default VFS, else it is not. + + On success, returns this object. Throws on error. + */ + capi.sqlite3_vfs.prototype.registerVfs = function(asDefault = false) { + if (!(this instanceof sqlite3.capi.sqlite3_vfs)) toss("Expecting a sqlite3_vfs-type argument."); + const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0); + if (rc) toss("sqlite3_vfs_register(", this, ") failed with rc", rc); + if (this.pointer !== capi.sqlite3_vfs_find(this.$zName)) toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", this); + return this; + }; + /** + A wrapper for + sqlite3.StructBinder.StructType.prototype.installMethods() or + registerVfs() to reduce installation of a VFS and/or its I/O + methods to a single call. + + Accepts an object which contains the properties "io" and/or + "vfs", each of which is itself an object with following properties: + + - `struct`: an sqlite3.StructBinder.StructType-type struct. This + must be a populated (except for the methods) object of type + sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the + "vfs" entry). + + - `methods`: an object mapping sqlite3_io_methods method names + (e.g. 'xClose') to JS implementations of those methods. The JS + implementations must be call-compatible with their native + counterparts. + + For each of those object, this function passes its (`struct`, + `methods`, (optional) `applyArgcCheck`) properties to + installMethods(). + + If the `vfs` entry is set then: + + - Its `struct` property's registerVfs() is called. The + `vfs` entry may optionally have an `asDefault` property, which + gets passed as the argument to registerVfs(). + + - If `struct.$zName` is falsy and the entry has a string-type + `name` property, `struct.$zName` is set to the C-string form of + that `name` value before registerVfs() is called. That string + gets added to the on-dispose state of the struct. + + On success returns this object. Throws on error. + */ + vfs.installVfs = function(opt) { + let count = 0; + const propList = ["io", "vfs"]; + for (const key of propList) { + const o = opt[key]; + if (o) { + ++count; + o.struct.installMethods(o.methods, !!o.applyArgcCheck); + if ("vfs" === key) { + if (!o.struct.$zName && "string" === typeof o.name) o.struct.addOnDispose(o.struct.$zName = wasm.allocCString(o.name)); + o.struct.registerVfs(!!o.asDefault); + } + } + } + if (!count) toss("Misuse: installVfs() options object requires at least", "one of:", propList); + return this; + }; + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + if (!sqlite3.wasm.exports.sqlite3_declare_vtab) return; + const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; + const vtab = Object.create(null); + sqlite3.vtab = vtab; + const sii = capi.sqlite3_index_info; + /** + If n is >=0 and less than this.$nConstraint, this function + returns either a WASM pointer to the 0-based nth entry of + this.$aConstraint (if passed a truthy 2nd argument) or an + sqlite3_index_info.sqlite3_index_constraint object wrapping that + address (if passed a falsy value or no 2nd argument). Returns a + falsy value if n is out of range. + */ + sii.prototype.nthConstraint = function(n, asPtr = false) { + if (n < 0 || n >= this.$nConstraint) return false; + const ptr = wasm.ptr.add(this.$aConstraint, sii.sqlite3_index_constraint.structInfo.sizeof * n); + return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr); + }; + /** + Works identically to nthConstraint() but returns state from + this.$aConstraintUsage, so returns an + sqlite3_index_info.sqlite3_index_constraint_usage instance + if passed no 2nd argument or a falsy 2nd argument. + */ + sii.prototype.nthConstraintUsage = function(n, asPtr = false) { + if (n < 0 || n >= this.$nConstraint) return false; + const ptr = wasm.ptr.add(this.$aConstraintUsage, sii.sqlite3_index_constraint_usage.structInfo.sizeof * n); + return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr); + }; + /** + If n is >=0 and less than this.$nOrderBy, this function + returns either a WASM pointer to the 0-based nth entry of + this.$aOrderBy (if passed a truthy 2nd argument) or an + sqlite3_index_info.sqlite3_index_orderby object wrapping that + address (if passed a falsy value or no 2nd argument). Returns a + falsy value if n is out of range. + */ + sii.prototype.nthOrderBy = function(n, asPtr = false) { + if (n < 0 || n >= this.$nOrderBy) return false; + const ptr = wasm.ptr.add(this.$aOrderBy, sii.sqlite3_index_orderby.structInfo.sizeof * n); + return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr); + }; + /** + Internal factory function for xVtab and xCursor impls. + */ + const __xWrapFactory = function(methodName, StructType) { + return function(ptr, removeMapping = false) { + if (0 === arguments.length) ptr = new StructType(); + if (ptr instanceof StructType) { + this.set(ptr.pointer, ptr); + return ptr; + } else if (!wasm.isPtr(ptr)) sqlite3.SQLite3Error.toss("Invalid argument to", methodName + "()"); + let rc = this.get(ptr); + if (removeMapping) this.delete(ptr); + return rc; + }.bind(/* @__PURE__ */ new Map()); + }; + /** + A factory function which implements a simple lifetime manager for + mappings between C struct pointers and their JS-level wrappers. + The first argument must be the logical name of the manager + (e.g. 'xVtab' or 'xCursor'), which is only used for error + reporting. The second must be the capi.XYZ struct-type value, + e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor. + + Returns an object with 4 methods: create(), get(), unget(), and + dispose(), plus a StructType member with the value of the 2nd + argument. The methods are documented in the body of this + function. + */ + const StructPtrMapper = function(name, StructType) { + const __xWrap = __xWrapFactory(name, StructType); + /** + This object houses a small API for managing mappings of (`T*`) + to StructType objects, specifically within the lifetime + requirements of sqlite3_module methods. + */ + return Object.assign(Object.create(null), { + StructType, + create: (ppOut) => { + const rc = __xWrap(); + wasm.pokePtr(ppOut, rc.pointer); + return rc; + }, + get: (pCObj) => __xWrap(pCObj), + unget: (pCObj) => __xWrap(pCObj, true), + dispose: (pCObj) => __xWrap(pCObj, true)?.dispose?.() + }); + }; + /** + A lifetime-management object for mapping `sqlite3_vtab*` + instances in sqlite3_module methods to capi.sqlite3_vtab + objects. + + The API docs are in the API-internal StructPtrMapper(). + */ + vtab.xVtab = StructPtrMapper("xVtab", capi.sqlite3_vtab); + /** + A lifetime-management object for mapping `sqlite3_vtab_cursor*` + instances in sqlite3_module methods to capi.sqlite3_vtab_cursor + objects. + + The API docs are in the API-internal StructPtrMapper(). + */ + vtab.xCursor = StructPtrMapper("xCursor", capi.sqlite3_vtab_cursor); + /** + Convenience form of creating an sqlite3_index_info wrapper, + intended for use in xBestIndex implementations. Note that the + caller is expected to call dispose() on the returned object + before returning. Though not _strictly_ required, as that object + does not own the pIdxInfo memory, it is nonetheless good form. + */ + vtab.xIndexInfo = (pIdxInfo) => new capi.sqlite3_index_info(pIdxInfo); + /** + Given an sqlite3_module method name and error object, this + function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof + sqlite3.WasmAllocError), else it returns its second argument. Its + intended usage is in the methods of a sqlite3_vfs or + sqlite3_module: + + ``` + try{ + let rc = ... + return rc; + }catch(e){ + return sqlite3.vtab.xError( + 'xColumn', e, sqlite3.capi.SQLITE_XYZ); + // where SQLITE_XYZ is some call-appropriate result code. + } + ``` + + If no 3rd argument is provided, its default depends on + the error type: + + - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM. + + - If err is an SQLite3Error then its `resultCode` property + is used. + + - If all else fails, capi.SQLITE_ERROR is used. + + If xError.errorReporter is a function, it is called in + order to report the error, else the error is not reported. + If that function throws, that exception is ignored. + */ + vtab.xError = function f(methodName, err, defaultRc) { + if (f.errorReporter instanceof Function) try { + f.errorReporter("sqlite3_module::" + methodName + "(): " + err.message); + } catch (e) {} + let rc; + if (err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM; + else if (arguments.length > 2) rc = defaultRc; + else if (err instanceof sqlite3.SQLite3Error) rc = err.resultCode; + return rc || capi.SQLITE_ERROR; + }; + vtab.xError.errorReporter = sqlite3.config.error.bind(sqlite3.config); + /** + A helper for sqlite3_vtab::xRowid() and xUpdate() + implementations. It must be passed the final argument to one of + those methods (an output pointer to an int64 row ID) and the + value to store at the output pointer's address. Returns the same + as wasm.poke() and will throw if the 1st or 2nd arguments + are invalid for that function. + + Example xRowid impl: + + ``` + const xRowid = (pCursor, ppRowid64)=>{ + const c = vtab.xCursor(pCursor); + vtab.xRowid(ppRowid64, c.myRowId); + return 0; + }; + ``` + */ + vtab.xRowid = (ppRowid64, value) => wasm.poke(ppRowid64, value, "i64"); + /** + A helper to initialize and set up an sqlite3_module object for + later installation into individual databases using + sqlite3_create_module(). Requires an object with the following + properties: + + - `methods`: an object containing a mapping of properties with + the C-side names of the sqlite3_module methods, e.g. xCreate, + xBestIndex, etc., to JS implementations for those functions. + Certain special-case handling is performed, as described below. + + - `catchExceptions` (default=false): if truthy, the given methods + are not mapped as-is, but are instead wrapped inside wrappers + which translate exceptions into result codes of SQLITE_ERROR or + SQLITE_NOMEM, depending on whether the exception is an + sqlite3.WasmAllocError. In the case of the xConnect and xCreate + methods, the exception handler also sets the output error + string to the exception's error string. + + - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If + not set, one will be created automatically. If the current + "this" is-a sqlite3_module then it is unconditionally used in + place of `struct`. + + - OPTIONAL `iVersion`: if set, it must be an integer value and it + gets assigned to the `$iVersion` member of the struct object. + If it's _not_ set, and the passed-in `struct` object's `$iVersion` + is 0 (the default) then this function attempts to define a value + for that property based on the list of methods it has. + + If `catchExceptions` is false, it is up to the client to ensure + that no exceptions escape the methods, as doing so would move + them through the C API, leading to undefined + behavior. (vtab.xError() is intended to assist in reporting + such exceptions.) + + Certain methods may refer to the same implementation. To simplify + the definition of such methods: + + - If `methods.xConnect` is `true` then the value of + `methods.xCreate` is used in its place, and vice versa. sqlite + treats xConnect/xCreate functions specially if they are exactly + the same function (same pointer value). + + - If `methods.xDisconnect` is true then the value of + `methods.xDestroy` is used in its place, and vice versa. + + This is to facilitate creation of those methods inline in the + passed-in object without requiring the client to explicitly get a + reference to one of them in order to assign it to the other + one. + + The `catchExceptions`-installed handlers will account for + identical references to the above functions and will install the + same wrapper function for both. + + The given methods are expected to return integer values, as + expected by the C API. If `catchExceptions` is truthy, the return + value of the wrapped function will be used as-is and will be + translated to 0 if the function returns a falsy value (e.g. if it + does not have an explicit return). If `catchExceptions` is _not_ + active, the method implementations must explicitly return integer + values. + + Throws on error. On success, returns the sqlite3_module object + (`this` or `opt.struct` or a new sqlite3_module instance, + depending on how it's called). + */ + vtab.setupModule = function(opt) { + let createdMod = false; + const mod = this instanceof capi.sqlite3_module ? this : opt.struct || (createdMod = new capi.sqlite3_module()); + try { + const methods = opt.methods || toss("Missing 'methods' object."); + for (const e of Object.entries({ + xConnect: "xCreate", + xDisconnect: "xDestroy" + })) { + const k = e[0], v = e[1]; + if (true === methods[k]) methods[k] = methods[v]; + else if (true === methods[v]) methods[v] = methods[k]; + } + if (opt.catchExceptions) { + const fwrap = function(methodName, func) { + if (["xConnect", "xCreate"].indexOf(methodName) >= 0) return function(pDb, pAux, argc, argv, ppVtab, pzErr) { + try { + return func(...arguments) || 0; + } catch (e) { + if (!(e instanceof sqlite3.WasmAllocError)) { + wasm.dealloc(wasm.peekPtr(pzErr)); + wasm.pokePtr(pzErr, wasm.allocCString(e.message)); + } + return vtab.xError(methodName, e); + } + }; + else return function(...args) { + try { + return func(...args) || 0; + } catch (e) { + return vtab.xError(methodName, e); + } + }; + }; + const mnames = [ + "xCreate", + "xConnect", + "xBestIndex", + "xDisconnect", + "xDestroy", + "xOpen", + "xClose", + "xFilter", + "xNext", + "xEof", + "xColumn", + "xRowid", + "xUpdate", + "xBegin", + "xSync", + "xCommit", + "xRollback", + "xFindFunction", + "xRename", + "xSavepoint", + "xRelease", + "xRollbackTo", + "xShadowName" + ]; + const remethods = Object.create(null); + for (const k of mnames) { + const m = methods[k]; + if (!(m instanceof Function)) continue; + else if ("xConnect" === k && methods.xCreate === m) remethods[k] = methods.xCreate; + else if ("xCreate" === k && methods.xConnect === m) remethods[k] = methods.xConnect; + else remethods[k] = fwrap(k, m); + } + mod.installMethods(remethods, false); + } else mod.installMethods(methods, !!opt.applyArgcCheck); + if (0 === mod.$iVersion) { + let v; + if ("number" === typeof opt.iVersion) v = opt.iVersion; + else if (mod.$xIntegrity) v = 4; + else if (mod.$xShadowName) v = 3; + else if (mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2; + else v = 1; + mod.$iVersion = v; + } + } catch (e) { + if (createdMod) createdMod.dispose(); + throw e; + } + return mod; + }; + /** + Equivalent to calling vtab.setupModule() with this sqlite3_module + object as the call's `this`. + */ + capi.sqlite3_module.prototype.setupModule = function(opt) { + return vtab.setupModule.call(this, opt); + }; + }); + /** + kvvfs - the Key/Value VFS - is an SQLite3 VFS which delegates + storage of its pages and metadata to a key-value store. + + It was conceived in order to support JS's localStorage and + sessionStorage objects. Its native implementation uses files as + key/value storage (one file per record) but the JS implementation + replaces a few methods so that it can use the aforementioned + objects as storage. + + It uses a bespoke ASCII encoding to store each db page as a + separate record and stores some metadata, like the db's unencoded + size and its journal, as individual records. + + kvvfs is significantly less efficient than a plain in-memory db but + it also, as a side effect of its design, offers a JSON-friendly + interchange format for exporting and importing databases. + + kvvfs is _not_ designed for heavy db loads. It is relatively + malloc()-heavy, having to de/allocate frequently, and it + spends much of its time converting the raw db pages into and out of + an ASCII encoding. + + But it _does_ work and is "performant enough" for db work of the + scale of a db which will fit within sessionStorage or localStorage + (just 2-3mb). + + "Version 2" extends it to support using Storage-like objects as + backing storage, Storage being the JS class which localStorage and + sessionStorage both derive from. This essentially moves the backing + store from whatever localStorage and sessionStorage use to an + in-memory object. + + This effort is primarily a stepping stone towards eliminating, if + it proves possible, the POSIX I/O API dependencies in SQLite's WASM + builds. That is: if this VFS works properly, it can be set as the + default VFS and we can eliminate the "unix" VFS from the JS/WASM + builds (as opposed to server-wise/WASI builds). That still, as of + 2025-11-23, a ways away, but it's the main driver for version 2 of + kvvfs. + + Version 2 remains compatible with version 1 databases and always + writes localStorage/sessionStorage metadata in the v1 format, so + such dbs can be manipulated freely by either version. For transient + storage objects (new in version 2), the format of its record keys + is simpified, requiring less space than v1 keys by eliding + redundant (in this context) info from the keys. + + Another benefit of v2 is its ability to export dbs into a + JSON-friendly (but not human-friendly) format. + + A potential, as-yet-unproven, benefit, would be the ability to plug + arbitrary Storage-compatible objects in so that clients could, + e.g. asynchronously post updates to db pages to some back-end for + backups. + */ + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + "use strict"; + const capi = sqlite3.capi, sqlite3_kvvfs_methods = capi.sqlite3_kvvfs_methods, KVVfsFile = capi.KVVfsFile, pKvvfs = sqlite3.capi.sqlite3_vfs_find("kvvfs"); + delete capi.sqlite3_kvvfs_methods; + delete capi.KVVfsFile; + if (!pKvvfs) return; + const util = sqlite3.util, wasm = sqlite3.wasm, toss3 = util.toss3, hop = (o, k) => Object.prototype.hasOwnProperty.call(o, k); + const kvvfsMethods = new sqlite3_kvvfs_methods(wasm.exports.sqlite3__wasm_kvvfs_methods()); + util.assert(32 <= kvvfsMethods.$nKeySize, "unexpected kvvfsMethods.$nKeySize: " + kvvfsMethods.$nKeySize); + /** + Most of the VFS-internal state. + */ + const cache = Object.assign(Object.create(null), { + rxJournalSuffix: /-journal$/, + zKeyJrnl: wasm.allocCString("jrnl"), + zKeySz: wasm.allocCString("sz"), + keySize: kvvfsMethods.$nKeySize, + buffer: Object.assign(Object.create(null), { + n: kvvfsMethods.$nBufferSize, + pool: Object.create(null) + }) + }); + /** + Returns a (cached) wasm.alloc()'d buffer of cache.buffer.n size, + throwing on OOM. + + We leak this one-time alloc because we've no better option. + sqlite3_vfs does not have a finalizer, so we've no place to hook + in the cleanup. We "could" extend sqlite3_shutdown() to have a + cleanup list for stuff like this but that function is never + used in JS, so it's hardly worth it. + */ + cache.memBuffer = (id = 0) => cache.buffer.pool[id] ??= wasm.alloc(cache.buffer.n); + /** Frees the buffer with the given id. */ + cache.memBufferFree = (id) => { + const b = cache.buffer.pool[id]; + if (b) { + wasm.dealloc(b); + delete cache.buffer.pool[id]; + } + }; + const noop = () => {}; + const debug = sqlite3.__isUnderTest ? (...args) => sqlite3.config.debug("kvvfs:", ...args) : noop; + const warn = (...args) => sqlite3.config.warn("kvvfs:", ...args); + const error = (...args) => sqlite3.config.error("kvvfs:", ...args); + /** + Implementation of JS's Storage interface for use as backing store + of the kvvfs. Storage is a native class and its constructor + cannot be legally called from JS, making it impossible to + directly subclass Storage. This class implements (only) the + Storage interface, to make it a drop-in replacement for + localStorage/sessionStorage. (Any behavioral discrepancies are to + be considered bugs.) + + This impl simply proxies a plain, prototype-less Object, suitable + for JSON-ing. + + Design note: Storage has a bit of an odd iteration-related + interface as does not (AFAIK) specify specific behavior regarding + modification during traversal. Because of that, this class does + some seemingly unnecessary things with its #keys member, deleting + and recreating it whenever a property index might be invalidated. + */ + class KVVfsStorage { + #map; + #keys; + #getKeys() { + return this.#keys ??= Object.keys(this.#map); + } + constructor() { + this.clear(); + } + key(n) { + const k = this.#getKeys(); + return n < k.length ? k[n] : null; + } + getItem(k) { + return this.#map[k] ?? null; + } + setItem(k, v) { + if (!hop(this.#map, k)) this.#keys = null; + this.#map[k] = "" + v; + } + removeItem(k) { + if (delete this.#map[k]) this.#keys = null; + } + clear() { + this.#map = Object.create(null); + this.#keys = null; + } + get length() { + return this.#getKeys().length; + } + } + /** True if v is the name of one of the special persistant Storage + objects. */ + const kvvfsIsPersistentName = (v) => "local" === v || "session" === v; + /** + Keys in kvvfs have a prefix of "kvvfs-NAME-", where NAME is the + db name. This key is redundant in JS but it's how kvvfs works (it + saves each key to a separate file, so needs a distinct namespace + per data source name). We retain this prefix in 'local' and + 'session' storage for backwards compatibility and so that they + can co-exist with client data in their storage, but we elide them + from "v2" storage, where they're superfluous. + */ + const kvvfsKeyPrefix = (v) => kvvfsIsPersistentName(v) ? "kvvfs-" + v + "-" : ""; + /** + Throws if storage name n (JS string) is not valid for use as a + storage name. Much of this goes back to kvvfs having a fixed + buffer size for its keys, and the storage name needing to be + encoded in the keys for local/session storage. + + The second argument must only be true when called from xOpen() - + it makes names with a "-journal" suffix legal. + */ + const validateStorageName = function(n, mayBeJournal = false) { + if (kvvfsIsPersistentName(n)) return; + const len = new Blob([n]).size; + if (!len) toss3(capi.SQLITE_MISUSE, "Empty name is not permitted."); + let maxLen = cache.keySize - 1; + if (cache.rxJournalSuffix.test(n)) { + if (!mayBeJournal) toss3(capi.SQLITE_MISUSE, "Storage names may not have a '-journal' suffix."); + } else if (["-wal", "-shm"].filter((v) => n.endsWith(v)).length) toss3(capi.SQLITE_MISUSE, "Storage names may not have a -wal or -shm suffix."); + else maxLen -= 8; + if (len > maxLen) toss3(capi.SQLITE_RANGE, "Storage name is too long. Limit =", maxLen); + let i; + for (i = 0; i < len; ++i) { + const ch = n.codePointAt(i); + if (ch < 32) toss3(capi.SQLITE_RANGE, "Illegal character (" + ch + "d) in storage name:", n); + } + }; + /** + Create a new instance of the objects which go into + cache.storagePool, with a refcount of 1. If passed a Storage-like + object as its second argument, it is used for the storage, + otherwise it creates a new KVVfsStorage object. + */ + const newStorageObj = (name, storage = void 0) => Object.assign(Object.create(null), { + jzClass: name, + refc: 1, + deleteAtRefc0: false, + storage: storage || new KVVfsStorage(), + keyPrefix: kvvfsKeyPrefix(name), + files: [], + listeners: void 0 + }); + /** + Public interface for kvvfs v2. The capi.sqlite3_js_kvvfs_...() + routines remain in place for v1. Some members of this class proxy + to those functions but use different default argument values in + some cases. + */ + const kvvfs = sqlite3.kvvfs = Object.create(null); + if (sqlite3.__isUnderTest) kvvfs.log = Object.assign(Object.create(null), { + xOpen: false, + xClose: false, + xWrite: false, + xRead: false, + xSync: false, + xAccess: false, + xFileControl: false, + xRcrdRead: false, + xRcrdWrite: false, + xRcrdDelete: false + }); + /** + Deletes the cache.storagePool entries for store (a + cache.storagePool entry) and its db/journal counterpart. + */ + const deleteStorage = function(store) { + const other = cache.rxJournalSuffix.test(store.jzClass) ? store.jzClass.replace(cache.rxJournalSuffix, "") : store.jzClass + "-journal"; + kvvfs?.log?.xClose && debug("cleaning up storage handles [", store.jzClass, other, "]", store); + delete cache.storagePool[store.jzClass]; + delete cache.storagePool[other]; + if (!sqlite3.__isUnderTest) { + delete store.storage; + delete store.refc; + } + }; + /** + Add both store.jzClass and store.jzClass+"-journal" + to cache,storagePool. + */ + const installStorageAndJournal = (store) => cache.storagePool[store.jzClass] = cache.storagePool[store.jzClass + "-journal"] = store; + /** + The public name of the current thread's transient storage + object. A storage object with this name gets preinstalled. + */ + const nameOfThisThreadStorage = "."; + /** + Map of JS-stringified KVVfsFile::zClass names to + reference-counted Storage objects. These objects are created in + xOpen(). Their refcount is decremented in xClose(), and the + record is destroyed if the refcount reaches 0. We refcount so + that concurrent active xOpen()s on a given name, and within a + given thread, use the same storage object. + */ + cache.storagePool = Object.assign(Object.create(null), { [nameOfThisThreadStorage]: newStorageObj(nameOfThisThreadStorage) }); + if (globalThis.Storage) { + if (globalThis.localStorage instanceof globalThis.Storage) cache.storagePool.local = newStorageObj("local", globalThis.localStorage); + if (globalThis.sessionStorage instanceof globalThis.Storage) cache.storagePool.session = newStorageObj("session", globalThis.sessionStorage); + } + cache.builtinStorageNames = Object.keys(cache.storagePool); + const isBuiltinName = (n) => cache.builtinStorageNames.indexOf(n) > -1; + for (const k of Object.keys(cache.storagePool)) { + const orig = cache.storagePool[k]; + cache.storagePool[k + "-journal"] = orig; + } + cache.setError = (e = void 0, dfltErrCode = capi.SQLITE_ERROR) => { + if (e) { + cache.lastError = e; + return e.resultCode | 0 || dfltErrCode; + } + delete cache.lastError; + return 0; + }; + cache.popError = () => { + const e = cache.lastError; + delete cache.lastError; + return e; + }; + /** Exception handler for notifyListeners(). */ + const catchForNotify = (e) => { + warn("kvvfs.listener handler threw:", e); + }; + const kvvfsDecode = wasm.exports.sqlite3__wasm_kvvfs_decode; + const kvvfsEncode = wasm.exports.sqlite3__wasm_kvvfs_encode; + /** + Listener events and their argument(s) (via the callback(ev) + ev.data member): + + 'open': number of opened handles on this storage. + + 'close': number of opened handles on this storage. + + 'write': key, value + + 'delete': key + + 'sync': true if it's from xSync(), false if it's from + xFileControl(). + + For efficiency's sake, all calls to this function should + be in the form: + + store.listeners && notifyListeners(...); + + Failing to do so will trigger an exceptin in this function (which + will be ignored but may produce a console warning). + */ + const notifyListeners = async function(eventName, store, ...args) { + try { + if (store.keyPrefix && args[0]) args[0] = args[0].replace(store.keyPrefix, ""); + let u8enc, z0, z1, wcache; + for (const ear of store.listeners) { + const ev = Object.create(null); + ev.storageName = store.jzClass; + ev.type = eventName; + ear.decodePages; + const f = ear.events[eventName]; + if (f) { + if (!ear.includeJournal && args[0] === "jrnl") continue; + if ("write" === eventName && ear.decodePages && +args[0] > 0) { + ev.data = [args[0]]; + if (wcache?.[args[0]]) { + ev.data[1] = wcache[args[0]]; + continue; + } + u8enc ??= new TextEncoder("utf-8"); + z0 ??= cache.memBuffer(10); + z1 ??= cache.memBuffer(11); + const u = u8enc.encode(args[1]); + const heap = wasm.heap8u(); + heap.set(u, Number(z0)); + heap[wasm.ptr.addn(z0, u.length)] = 0; + const rc = kvvfsDecode(z0, z1, cache.buffer.n); + if (rc > 0) { + wcache ??= Object.create(null); + wcache[args[0]] = ev.data[1] = heap.slice(Number(z1), wasm.ptr.addn(z1, rc)); + } else continue; + } else ev.data = args.length ? args.length === 1 ? args[0] : args : void 0; + try { + f(ev)?.catch?.(catchForNotify); + } catch (e) { + warn("notifyListeners [", store.jzClass, "]", eventName, e); + } + } + } + } catch (e) { + catchForNotify(e); + } + }; + /** + Returns the storage object mapped to the given string zClass + (C-string pointer or JS string). + */ + const storageForZClass = (zClass) => "string" === typeof zClass ? cache.storagePool[zClass] : cache.storagePool[wasm.cstrToJs(zClass)]; + const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKey; + /** + Returns a C string from kvvfsMakeKey() OR returns zKey. In the + former case the memory is static, so must be copied before a + second call. zKey MUST be a pointer passed to a VFS/file method, + to allow us to avoid an alloc and/or an snprintf(). It requires + C-string arguments for zClass and zKey. zClass may be NULL but + zKey may not. + */ + const zKeyForStorage = (store, zClass, zKey) => { + return zClass && store.keyPrefix ? kvvfsMakeKey(zClass, zKey) : zKey; + }; + const jsKeyForStorage = (store, zClass, zKey) => wasm.cstrToJs(zKeyForStorage(store, zClass, zKey)); + const storageGetDbSize = (store) => +store.storage.getItem(store.keyPrefix + "sz"); + /** + sqlite3_file pointers => objects, each of which has: + + .file = KVVfsFile instance + + .jzClass = JS-string form of f.$zClass + + .storage = Storage object. It is shared between a db and its + journal. + */ + const pFileHandles = /* @__PURE__ */ new Map(); + /** + Original WASM functions for methods we partially override. + */ + const originalMethods = { + vfs: Object.create(null), + ioDb: Object.create(null), + ioJrnl: Object.create(null) + }; + const pVfs = new capi.sqlite3_vfs(kvvfsMethods.$pVfs); + const pIoDb = new capi.sqlite3_io_methods(kvvfsMethods.$pIoDb); + const pIoJrnl = new capi.sqlite3_io_methods(kvvfsMethods.$pIoJrnl); + const recordHandler = Object.create(null); + const kvvfsInternal = Object.assign(Object.create(null), { + pFileHandles, + cache, + storageForZClass, + KVVfsStorage, + disablePageSizeChange: true + }); + if (kvvfs.log) kvvfs.internal = kvvfsInternal; + /** + Implementations for members of the object referred to by + sqlite3__wasm_kvvfs_methods(). We swap out some native + implementations with these so that we can use JS Storage for + their backing store. + */ + const methodOverrides = { + recordHandler: { + xRcrdRead: (zClass, zKey, zBuf, nBuf) => { + try { + const jzClass = wasm.cstrToJs(zClass); + const store = storageForZClass(jzClass); + if (!store) return -1; + const jXKey = jsKeyForStorage(store, zClass, zKey); + kvvfs?.log?.xRcrdRead && warn("xRcrdRead", jzClass, jXKey, nBuf, store); + const jV = store.storage.getItem(jXKey); + if (null === jV) return -1; + const nV = jV.length; + if (nBuf <= 0) return nV; + else if (1 === nBuf) { + wasm.poke(zBuf, 0); + return nV; + } + if (nBuf + 1 < nV) toss3(capi.SQLITE_RANGE, "xRcrdRead()", jzClass, jXKey, "input buffer is too small: need", nV, "but have", nBuf); + const zV = cache.memBuffer(0); + const heap = wasm.heap8(); + let i; + for (i = 0; i < nV; ++i) heap[wasm.ptr.add(zV, i)] = jV.codePointAt(i) & 255; + heap.copyWithin(Number(zBuf), Number(zV), wasm.ptr.addn(zV, i)); + heap[wasm.ptr.add(zBuf, nV)] = 0; + return nBuf; + } catch (e) { + error("kvrecordRead()", e); + cache.setError(e); + return -2; + } + }, + xRcrdWrite: (zClass, zKey, zData) => { + try { + const store = storageForZClass(zClass); + const jxKey = jsKeyForStorage(store, zClass, zKey); + const jData = wasm.cstrToJs(zData); + kvvfs?.log?.xRcrdWrite && warn("xRcrdWrite", jxKey, store); + store.storage.setItem(jxKey, jData); + store.listeners && notifyListeners("write", store, jxKey, jData); + return 0; + } catch (e) { + error("kvrecordWrite()", e); + return cache.setError(e, capi.SQLITE_IOERR); + } + }, + xRcrdDelete: (zClass, zKey) => { + try { + const store = storageForZClass(zClass); + const jxKey = jsKeyForStorage(store, zClass, zKey); + kvvfs?.log?.xRcrdDelete && warn("xRcrdDelete", jxKey, store); + store.storage.removeItem(jxKey); + store.listeners && notifyListeners("delete", store, jxKey); + return 0; + } catch (e) { + error("kvrecordDelete()", e); + return cache.setError(e, capi.SQLITE_IOERR); + } + } + }, + vfs: { + xOpen: function(pProtoVfs, zName, pProtoFile, flags, pOutFlags) { + cache.popError(); + let zToFree; + try { + if (!zName) { + zToFree = wasm.allocCString("" + pProtoFile + "." + (Math.random() * 1e5 | 0)); + zName = zToFree; + } + const jzClass = wasm.cstrToJs(zName); + kvvfs?.log?.xOpen && debug("xOpen", jzClass, "flags =", flags); + validateStorageName(jzClass, true); + if (flags & (capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_TEMP_DB | capi.SQLITE_OPEN_TRANSIENT_DB) && cache.rxJournalSuffix.test(jzClass)) toss3(capi.SQLITE_ERROR, "DB files may not have a '-journal' suffix."); + let s = storageForZClass(jzClass); + if (!s && !(flags & capi.SQLITE_OPEN_CREATE)) toss3(capi.SQLITE_ERROR, "Storage not found:", jzClass); + const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile, flags, pOutFlags); + if (rc) return rc; + let deleteAt0 = !!(capi.SQLITE_OPEN_DELETEONCLOSE & flags); + if (wasm.isPtr(arguments[1])) { + if (capi.sqlite3_uri_boolean(zName, "delete-on-close", 0)) deleteAt0 = true; + } + const f = new KVVfsFile(pProtoFile); + util.assert(f.$zClass, "Missing f.$zClass"); + f.addOnDispose(zToFree); + zToFree = void 0; + if (s) { + ++s.refc; + s.files.push(f); + wasm.poke32(pOutFlags, flags); + } else { + wasm.poke32(pOutFlags, flags | capi.SQLITE_OPEN_CREATE); + util.assert(!f.$isJournal, "Opening a journal before its db? " + jzClass); + const nm = jzClass.replace(cache.rxJournalSuffix, ""); + s = newStorageObj(nm); + installStorageAndJournal(s); + s.files.push(f); + s.deleteAtRefc0 = deleteAt0; + kvvfs?.log?.xOpen && debug("xOpen installed storage handle [", nm, nm + "-journal", "]", s); + } + pFileHandles.set(pProtoFile, { + store: s, + file: f, + jzClass + }); + s.listeners && notifyListeners("open", s, s.files.length); + return 0; + } catch (e) { + warn("xOpen:", e); + return cache.setError(e); + } finally { + zToFree && wasm.dealloc(zToFree); + } + }, + xDelete: function(pVfs, zName, iSyncFlag) { + cache.popError(); + try { + const jzName = wasm.cstrToJs(zName); + if (cache.rxJournalSuffix.test(jzName)) recordHandler.xRcrdDelete(zName, cache.zKeyJrnl); + return 0; + } catch (e) { + warn("xDelete", e); + return cache.setError(e); + } + }, + xAccess: function(pProtoVfs, zPath, flags, pResOut) { + cache.popError(); + try { + const s = storageForZClass(zPath); + const jzPath = s?.jzClass || wasm.cstrToJs(zPath); + if (kvvfs?.log?.xAccess) debug("xAccess", jzPath, "flags =", flags, "*pResOut =", wasm.peek32(pResOut), "store =", s); + if (!s) + /** The xAccess method returns [SQLITE_OK] on success or some + ** non-zero error code if there is an I/O error or if the name of + ** the file given in the second argument is illegal. + */ + try { + validateStorageName(jzPath); + } catch (e) { + wasm.poke32(pResOut, 0); + return 0; + } + if (s) { + const key = s.keyPrefix + (cache.rxJournalSuffix.test(jzPath) ? "jrnl" : "1"); + const res = s.storage.getItem(key) ? 0 : 1; + wasm.poke32(pResOut, res); + } else wasm.poke32(pResOut, 0); + return 0; + } catch (e) { + error("xAccess", e); + return cache.setError(e); + } + }, + xRandomness: function(pVfs, nOut, pOut) { + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; + return nOut; + }, + xGetLastError: function(pVfs, nOut, pOut) { + const e = cache.popError(); + debug("xGetLastError", e); + if (e) { + const scope = wasm.scopedAllocPush(); + try { + const [cMsg, n] = wasm.scopedAllocCString(e.message, true); + wasm.cstrncpy(pOut, cMsg, nOut); + if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); + debug("set xGetLastError", e.message); + return e.resultCode | 0 || capi.SQLITE_IOERR; + } catch (e) { + return capi.SQLITE_NOMEM; + } finally { + wasm.scopedAllocPop(scope); + } + } + return 0; + } + }, + ioDb: { + xClose: function(pFile) { + cache.popError(); + try { + const h = pFileHandles.get(pFile); + kvvfs?.log?.xClose && debug("xClose", pFile, h); + if (h) { + pFileHandles.delete(pFile); + const s = h.store; + s.files = s.files.filter((v) => v !== h.file); + if (--s.refc <= 0 && s.deleteAtRefc0) deleteStorage(s); + originalMethods.ioDb.xClose(pFile); + h.file.dispose(); + s.listeners && notifyListeners("close", s, s.files.length); + } + return 0; + } catch (e) { + error("xClose", e); + return cache.setError(e); + } + }, + xFileControl: function(pFile, opId, pArg) { + cache.popError(); + try { + const h = pFileHandles.get(pFile); + util.assert(h, "Missing KVVfsFile handle"); + kvvfs?.log?.xFileControl && debug("xFileControl", h, "op =", opId); + if (opId === capi.SQLITE_FCNTL_PRAGMA && kvvfsInternal.disablePageSizeChange) { + const zName = wasm.peekPtr(wasm.ptr.add(pArg, wasm.ptr.size)); + if ("page_size" === wasm.cstrToJs(zName)) { + kvvfs?.log?.xFileControl && debug("xFileControl pragma", wasm.cstrToJs(zName)); + const zVal = wasm.peekPtr(wasm.ptr.add(pArg, 2 * wasm.ptr.size)); + if (zVal) { + kvvfs?.log?.xFileControl && warn("xFileControl pragma", h, "NOT setting page size to", wasm.cstrToJs(zVal)); + h.file.$szPage = -1; + return 0; + } else if (h.file.$szPage > 0) { + kvvfs?.log?.xFileControl && warn("xFileControl", h, "getting page size", h.file.$szPage); + wasm.pokePtr(pArg, wasm.allocCString("" + h.file.$szPage)); + return 0; + } + } + } + const rc = originalMethods.ioDb.xFileControl(pFile, opId, pArg); + if (0 == rc && capi.SQLITE_FCNTL_SYNC === opId) h.store.listeners && notifyListeners("sync", h.store, false); + return rc; + } catch (e) { + error("xFileControl", e); + return cache.setError(e); + } + }, + xSync: function(pFile, flags) { + cache.popError(); + try { + const h = pFileHandles.get(pFile); + kvvfs?.log?.xSync && debug("xSync", h); + util.assert(h, "Missing KVVfsFile handle"); + const rc = originalMethods.ioDb.xSync(pFile, flags); + if (0 == rc && h.store.listeners) notifyListeners("sync", h.store, true); + return rc; + } catch (e) { + error("xSync", e); + return cache.setError(e); + } + }, + xRead: function(pFile, pTgt, n, iOff64) { + cache.popError(); + try { + if (kvvfs?.log?.xRead) { + const h = pFileHandles.get(pFile); + util.assert(h, "Missing KVVfsFile handle"); + debug("xRead", n, iOff64, h); + } + return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); + } catch (e) { + error("xRead", e); + return cache.setError(e); + } + }, + xWrite: function(pFile, pSrc, n, iOff64) { + cache.popError(); + try { + if (kvvfs?.log?.xWrite) { + const h = pFileHandles.get(pFile); + util.assert(h, "Missing KVVfsFile handle"); + debug("xWrite", n, iOff64, h); + } + return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); + } catch (e) { + error("xWrite", e); + return cache.setError(e); + } + } + }, + ioJrnl: { xClose: true } + }; + debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, kvvfsMethods, capi.sqlite3_file.structInfo, KVVfsFile.structInfo); + try { + util.assert(cache.buffer.n > 1024 * 129, "Heap buffer is not large enough"); + for (const e of Object.entries(methodOverrides.recordHandler)) { + const k = e[0], f = e[1]; + recordHandler[k] = f; + kvvfsMethods[kvvfsMethods.memberKey(k)] = wasm.installFunction(kvvfsMethods.memberSignature(k), f); + } + for (const e of Object.entries(methodOverrides.vfs)) { + const k = e[0], f = e[1], km = pVfs.memberKey(k), member = pVfs.structInfo.members[k] || util.toss("Missing pVfs.structInfo[", k, "]"); + originalMethods.vfs[k] = wasm.functionEntry(pVfs[km]); + pVfs[km] = wasm.installFunction(member.signature, f); + } + for (const e of Object.entries(methodOverrides.ioDb)) { + const k = e[0], f = e[1], km = pIoDb.memberKey(k); + originalMethods.ioDb[k] = wasm.functionEntry(pIoDb[km]) || util.toss("Missing native pIoDb[", km, "]"); + pIoDb[km] = wasm.installFunction(pIoDb.memberSignature(k), f); + } + for (const e of Object.entries(methodOverrides.ioJrnl)) { + const k = e[0], f = e[1], km = pIoJrnl.memberKey(k); + originalMethods.ioJrnl[k] = wasm.functionEntry(pIoJrnl[km]) || util.toss("Missing native pIoJrnl[", km, "]"); + if (true === f) pIoJrnl[km] = pIoDb[km] || util.toss("Missing copied pIoDb[", km, "]"); + else pIoJrnl[km] = wasm.installFunction(pIoJrnl.memberSignature(k), f); + } + } finally { + kvvfsMethods.dispose(); + pVfs.dispose(); + pIoDb.dispose(); + pIoJrnl.dispose(); + } + /** + Clears all storage used by the kvvfs DB backend, deleting any + DB(s) stored there. + + Its argument must be the name of a kvvfs storage object: + + - 'session' + - 'local' + - '' - see below. + - A transient kvvfs storage object name. + + In the first two cases, only sessionStorage resp. localStorage is + cleared. An empty string resolves to both 'local' and 'session' + storage. + + Returns the number of entries cleared. + + As of kvvfs version 2: + + This API is available in Worker threads but does not have access + to localStorage or sessionStorage in them. Prior versions did not + include this API in Worker threads. + + Differences in this function in version 2: + + - It accepts an arbitrary storage name. In v1 this was a silent + no-op for any names other than ('local','session',''). + + - It throws if a db currently has the storage opened UNLESS the + storage object is localStorage or sessionStorage. That version 1 + did not throw for this case was due to an architectural + limitation which has since been overcome, but removal of + JsStorageDb.prototype.clearStorage() would be a backwards compatibility + break, so this function permits wiping the storage for those two + cases even if they are opened. Use with case. + */ + const sqlite3_js_kvvfs_clear = function callee(which) { + if ("" === which) return callee("local") + callee("session"); + const store = storageForZClass(which); + if (!store) return 0; + if (store.files.length) if (globalThis.localStorage === store.storage || globalThis.sessionStorage === store.storage) {} else toss3(capi.SQLITE_ACCESS, "Cannot clear in-use database storage."); + const s = store.storage; + const toRm = []; + let i, n = s.length; + for (i = 0; i < n; ++i) { + const k = s.key(i); + if (!store.keyPrefix || k.startsWith(store.keyPrefix)) toRm.push(k); + } + toRm.forEach((kk) => s.removeItem(kk)); + return toRm.length; + }; + /** + This routine estimates the approximate amount of + storage used by the given kvvfs back-end. + + Its arguments are as documented for sqlite3_js_kvvfs_clear(), + only the operation this performs is different. + + The returned value is twice the "length" value of every matching + key and value, noting that JavaScript stores each character in 2 + bytes. + + The returned size is not authoritative from the perspective of + how much data can fit into localStorage and sessionStorage, as + the precise algorithms for determining those limits are + unspecified and may include per-entry overhead invisible to + clients. + */ + const sqlite3_js_kvvfs_size = function callee(which) { + if ("" === which) return callee("local") + callee("session"); + const store = storageForZClass(which); + if (!store) return 0; + const s = store.storage; + let i, sz = 0; + for (i = 0; i < s.length; ++i) { + const k = s.key(i); + if (!store.keyPrefix || k.startsWith(store.keyPrefix)) { + sz += k.length; + sz += s.getItem(k).length; + } + } + return sz * 2; + }; + /** + Exports a kvvfs storage object to an object, optionally + JSON-friendly. + + Usages: + + thisfunc(storageName); + thisfunc(options); + + In the latter case, the options object must be an object with + the following properties: + + - "name" (string) required. The storage to export. + + - "decodePages" (bool=false). If true, the .pages result property + holdes Uint8Array objects holding the raw binary-format db + pages. The default is to use kvvfs-encoded string pages + (JSON-friendly). + + - "includeJournal" (bool=false). If true and the db has a current + journal, it is exported as well. (Kvvfs journals are stored as a + single record within the db's storage object.) + + The returned object is structured as follows... + + - "name": the name of the storage. This is 'local' or 'session' + for localStorage resp. sessionStorage, and an arbitrary name for + transient storage. This propery may be changed before passing + this object to sqlite3_js_kvvfs_import() in order to + import into a different storage object. + + - "timestamp": the time this function was called, in Unix + epoch milliseconds. + + - "size": the unencoded db size. + + - "journal": if options.includeJournal is true and this db has a + journal, it is stored as a string here, otherwise this property + is not set. + + - "pages": An array holding the raw encoded db pages in their + proper order. + + Throws if this db is not opened. + + The encoding of the underlying database is not part of this + interface - it is simply passed on as-is. Interested parties are + directed to src/os_kv.c in the SQLite source tree, with the + caveat that that code also does not offer a public interface. + i.e. the encoding is a private implementation detail of kvvfs. + The format may be changed in the future but kvvfs will continue + to support the current form. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_export = function callee(...args) { + let opt; + if (1 === args.length && "object" === typeof args[0]) opt = args[0]; + else if (args.length) opt = Object.assign(Object.create(null), { name: args[0] }); + const store = opt ? storageForZClass(opt.name) : null; + if (!store) toss3(capi.SQLITE_NOTFOUND, "There is no kvvfs storage named", opt?.name); + const s = store.storage; + const rc = Object.assign(Object.create(null), { + name: store.jzClass, + timestamp: Date.now(), + pages: [] + }); + const pages = Object.create(null); + const keyPrefix = store.keyPrefix; + const rxTail = keyPrefix ? /^kvvfs-[^-]+-(\w+)/ : void 0; + let i = 0, n = s.length; + for (; i < n; ++i) { + const k = s.key(i); + if (!keyPrefix || k.startsWith(keyPrefix)) { + let kk = (keyPrefix ? rxTail.exec(k) : void 0)?.[1] ?? k; + switch (kk) { + case "jrnl": + if (opt.includeJournal) rc.journal = s.getItem(k); + break; + case "sz": + rc.size = +s.getItem(k); + break; + default: + kk = +kk; + if (!util.isInt32(kk) || kk <= 0) toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: " + k); + if (opt.decodePages) { + const spg = s.getItem(k), n = spg.length, z = cache.memBuffer(0), zDec = cache.memBuffer(1), heap = wasm.heap8u(); + let i = 0; + for (; i < n; ++i) heap[wasm.ptr.add(z, i)] = spg.codePointAt(i) & 255; + heap[wasm.ptr.add(z, i)] = 0; + const nDec = kvvfsDecode(z, zDec, cache.buffer.n); + pages[kk] = heap.slice(Number(zDec), wasm.ptr.addn(zDec, nDec)); + } else pages[kk] = s.getItem(k); + break; + } + } + } + if (opt.decodePages) cache.memBufferFree(1); + Object.keys(pages).map((v) => +v).sort().forEach((v) => rc.pages.push(pages[v])); + return rc; + }; + /** + The counterpart of sqlite3_js_kvvfs_export(). Its + argument must be the result of that function() or + a compatible one. + + This either replaces the contents of an existing transient + storage object or installs one named exp.name, setting + the storage's db contents to that of the exp object. + + Throws on error. Error conditions include: + + - The given storage object is currently opened by any db. + Performing this page-by-page import would invoke undefined + behavior on them. + + - Malformed input object. + + If it throws after starting the import then it clears the storage + before returning, to avoid leaving the db in an undefined + state. It may throw for any of the above-listed conditions before + reaching that step, in which case the db is not modified. If + exp.name refers to a new storage name then if it throws, the name + does not get installed. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_import = function(exp, overwrite = false) { + if (!exp?.timestamp || !exp.name || void 0 === exp.size || !Array.isArray(exp.pages)) toss3(capi.SQLITE_MISUSE, "Malformed export object."); + else if (!exp.size || exp.size !== (exp.size | 0) || exp.size >= 2147483647) toss3(capi.SQLITE_RANGE, "Invalid db size: " + exp.size); + validateStorageName(exp.name); + let store = storageForZClass(exp.name); + const isNew = !store; + if (store) { + if (!overwrite) toss3(capi.SQLITE_ACCESS, "Storage '" + exp.name + "' already exists and", "overwrite was not specified."); + else if (!store.files || !store.jzClass) toss3(capi.SQLITE_ERROR, "Internal storage object", exp.name, "seems to be malformed."); + else if (store.files.length) toss3(capi.SQLITE_IOERR_ACCESS, "Cannot import db storage while it is in use."); + sqlite3_js_kvvfs_clear(exp.name); + } else store = newStorageObj(exp.name); + const keyPrefix = kvvfsKeyPrefix(exp.name); + let zEnc; + try { + const s = store.storage; + s.setItem(keyPrefix + "sz", exp.size); + if (exp.journal) s.setItem(keyPrefix + "jrnl", exp.journal); + if (exp.pages[0] instanceof Uint8Array) exp.pages.forEach((u, ndx) => { + const n = u.length; + zEnc ??= cache.memBuffer(1); + const zBin = cache.memBuffer(0), heap = wasm.heap8u(); + heap.set(u, Number(zBin)); + heap[wasm.ptr.addn(zBin, n)] = 0; + const rc = kvvfsEncode(zBin, n, zEnc); + util.assert(rc < cache.buffer.n, "Impossibly long output - possibly smashed the heap"); + util.assert(0 === wasm.peek8(wasm.ptr.add(zEnc, rc)), "Expecting NUL-terminated encoded output"); + const jenc = wasm.cstrToJs(zEnc); + s.setItem(keyPrefix + (ndx + 1), jenc); + }); + else if (exp.pages[0]) exp.pages.forEach((v, ndx) => s.setItem(keyPrefix + (ndx + 1), v)); + if (isNew) installStorageAndJournal(store); + } catch { + if (!isNew) try { + sqlite3_js_kvvfs_clear(exp.name); + } catch (ee) {} + } finally { + if (zEnc) cache.memBufferFree(1); + } + return this; + }; + /** + If no kvvfs storage exists with the given name, one is + installed. If one exists, its reference count is increased so + that it won't be freed by the closing of a database or journal + file. + + Throws if the name is not valid for a new storage object. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_reserve = function(name) { + let store = storageForZClass(name); + if (store) { + ++store.refc; + return; + } + validateStorageName(name); + installStorageAndJournal(newStorageObj(name)); + }; + /** + Conditionally "unlinks" a kvvfs storage object, reducing its + reference count by 1. + + This is a no-op if name ends in "-journal" or refers to a + built-in storage object. + + It will not lower the refcount below the number of + currently-opened db/journal files for the storage (so that it + cannot delete it out from under them). + + If the refcount reaches 0 then the storage object is + removed. + + Returns true if it reduces the refcount, else false. A result of + true does not necessarily mean that the storage unit was removed, + just that its refcount was lowered. Similarly, a result of false + does not mean that the storage is removed - it may still have + opened handles. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_unlink = function(name) { + const store = storageForZClass(name); + if (!store || kvvfsIsPersistentName(store.jzClass) || isBuiltinName(store.jzClass) || cache.rxJournalSuffix.test(name)) return false; + if (store.refc > store.files.length || 0 === store.files.length) { + if (--store.refc <= 0) deleteStorage(store); + return true; + } + return false; + }; + /** + Adds an event listener to a kvvfs storage object. The idea is + that this can be used to asynchronously back up one kvvfs storage + object to another or another channel entirely. (The caveat in the + latter case is that kvvfs's format is not readily consumable by + downstream code.) + + Its argument must be an object with the following properties: + + - storage: the name of the kvvfs storage object. + + - reserve [=false]: if true, sqlite3_js_kvvfs_reserve() is used + to ensure that the storage exists if it does not already. + If this is false and the storage does not exist then an + exception is thrown. + + - events: an object which may have any of the following + callback function properties: open, close, write, delete. + + - decodePages [=false]: if true, write events will receive each + db page write in the form of a Uint8Array holding the raw binary + db page. The default is to emit the kvvfs-format page because it + requires no extra work, we already have it in hand, and it's + often smaller. It's not great for interchange, though. + + - includeJournal [=false]: if true, writes and deletes of + "jrnl" records are included. If false, no events are sent + for journal updates. + + Passing the same object to sqlite3_js_kvvfs_unlisten() will + remove the listener. + + Each one of the events callbacks will be called asynchronously + when the given storage performs those operations. They may be + asynchronous functions but are not required to be (the events are + fired async either way, but making the event callbacks async may + be advantageous when multiple listeners are involved). All + exceptions, including those via Promises, are ignored but may (or + may not) trigger warning output on the console. + + Each callback gets passed a single object with the following + properties: + + .type = the same as the name of the callback + + .storageName = the name of the storage object + + .data = callback-dependent: + + - 'open' and 'close' get an integer, the number of + currently-opened handles on the storage. + + - 'write' gets a length-two array holding the key and value which + were written. The key is always a string, even if it's a db page + number. For db-page records, the value's type depends on + opt.decodePages. All others, including the journal, are strings. + (The journal, being a kvvfs-specific format, is delivered in + that same JSON-friendly format.) More details below. + + - 'delete' gets the string-type key of the deleted record. + + - 'sync' gets a boolean value: true if it was triggered by db + file's xSync(), false if it was triggered by xFileControl(). The + latter triggers before the xSync() and also triggers if the DB + has PRAGMA SYNCHRONOUS=OFF (in which case xSync() is not + triggered). + + The key/value arguments to 'write', and key argument to 'delete', + are in one of the following forms: + + - 'sz' = the unencoded db size as a string. This specific key is + key is never deleted, so is only ever passed to 'write' events. + + - 'jrnl' = the current db journal as a kvvfs-encoded string. This + journal format is not useful anywhere except in the kvvfs + internals. These events are not fired if opt.includeJournal is + false. + + - '[1-9][0-9]*' (a db page number) = Its type depends on + opt.decodePages. These may be written and deleted in arbitrary + order. + + Design note: JS has StorageEvents but only in the main thread, + which is why the listeners are not based on that. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_listen = function(opt) { + if (!opt || "object" !== typeof opt) toss3(capi.SQLITE_MISUSE, "Expecting a listener object."); + let store = storageForZClass(opt.storage); + if (!store) if (opt.storage && opt.reserve) { + sqlite3_js_kvvfs_reserve(opt.storage); + store = storageForZClass(opt.storage); + util.assert(store, "Unexpectedly cannot fetch reserved storage " + opt.storage); + } else toss3(capi.SQLITE_NOTFOUND, "No such storage:", opt.storage); + if (opt.events) (store.listeners ??= []).push(opt); + }; + /** + Removes the kvvfs event listeners for the given options + object. It must be passed the same object instance which was + passed to sqlite3_js_kvvfs_listen(). + + This has no side effects if opt is invalid or is not a match for + any listeners. + + Return true if it unregisters its argument, else false. + + Added in version 3.52.0. + */ + const sqlite3_js_kvvfs_unlisten = function(opt) { + const store = storageForZClass(opt?.storage); + if (store?.listeners && opt.events) { + const n = store.listeners.length; + store.listeners = store.listeners.filter((v) => v !== opt); + const rc = n > store.listeners.length; + if (!store.listeners.length) store.listeners = void 0; + return rc; + } + return false; + }; + sqlite3.kvvfs.reserve = sqlite3_js_kvvfs_reserve; + sqlite3.kvvfs.import = sqlite3_js_kvvfs_import; + sqlite3.kvvfs.export = sqlite3_js_kvvfs_export; + sqlite3.kvvfs.unlink = sqlite3_js_kvvfs_unlink; + sqlite3.kvvfs.listen = sqlite3_js_kvvfs_listen; + sqlite3.kvvfs.unlisten = sqlite3_js_kvvfs_unlisten; + sqlite3.kvvfs.exists = (name) => !!storageForZClass(name); + sqlite3.kvvfs.estimateSize = sqlite3_js_kvvfs_size; + sqlite3.kvvfs.clear = sqlite3_js_kvvfs_clear; + if (globalThis.Storage) { + /** + Prior to version 2, kvvfs was only available in the main + thread. We retain that for the v1 APIs, exposing them only in + the main UI thread. As of version 2, kvvfs is available in all + threads but only via its v2 interface (sqlite3.kvvfs). + + These versions have a default argument value of "" which the v2 + versions lack. + */ + capi.sqlite3_js_kvvfs_size = (which = "") => sqlite3_js_kvvfs_size(which); + capi.sqlite3_js_kvvfs_clear = (which = "") => sqlite3_js_kvvfs_clear(which); + } + if (sqlite3.oo1?.DB) { + /** + Functionally equivalent to DB(storageName,'c','kvvfs') except + that it throws if the given storage name is not one of 'local' + or 'session'. + + As of version 3.46, the argument may optionally be an options + object in the form: + + { + filename: 'session'|'local', + ... etc. (all options supported by the DB ctor) + } + + noting that the 'vfs' option supported by main DB + constructor is ignored here: the vfs is always 'kvvfs'. + */ + const DB = sqlite3.oo1.DB; + sqlite3.oo1.JsStorageDb = function(storageName = sqlite3.oo1.JsStorageDb.defaultStorageName) { + const opt = DB.dbCtorHelper.normalizeArgs(...arguments); + opt.vfs = "kvvfs"; + switch (opt.filename) { + case ":sessionStorage:": + opt.filename = "session"; + break; + case ":localStorage:": + opt.filename = "local"; + break; + } + const m = /(file:(\/\/)?)([^?]+)/.exec(opt.filename); + validateStorageName(m ? m[3] : opt.filename); + DB.dbCtorHelper.call(this, opt); + }; + sqlite3.oo1.JsStorageDb.defaultStorageName = cache.storagePool.session ? "session" : nameOfThisThreadStorage; + const jdb = sqlite3.oo1.JsStorageDb; + jdb.prototype = Object.create(DB.prototype); + jdb.clearStorage = sqlite3_js_kvvfs_clear; + /** + DEPRECATED: the inherited method of this name (as opposed to + the "static" class method) is deprecated with version 2 of + kvvfs. This function will, for backwards comaptibility, + continue to work with localStorage and sessionStorage, but will + throw for all other storage because they are opened. Version 1 + was not capable of recognizing that the storage was opened so + permitted wiping it out at any time, but that was arguably a + bug. + + Clears this database instance's storage or throws if this + instance has been closed. Returns the number of + database pages which were cleaned up. + */ + jdb.prototype.clearStorage = function() { + return jdb.clearStorage(this.affirmOpen().dbFilename(), true); + }; + /** Equivalent to sqlite3_js_kvvfs_size(). */ + jdb.storageSize = sqlite3_js_kvvfs_size; + /** + Returns the _approximate_ number of bytes this database takes + up in its storage or throws if this instance has been closed. + */ + jdb.prototype.storageSize = function() { + return jdb.storageSize(this.affirmOpen().dbFilename(), true); + }; + } + if (sqlite3.__isUnderTest && sqlite3.vtab) { + /** + An eponymous vtab for inspecting the kvvfs state. This is only + intended for use in testing and development, not part of the + public API. + */ + const cols = Object.assign(Object.create(null), { + rowid: { type: "INTEGER" }, + name: { type: "TEXT" }, + nRef: { type: "INTEGER" }, + nOpen: { type: "INTEGER" }, + isTransient: { type: "INTEGER" }, + dbSize: { type: "INTEGER" } + }); + Object.keys(cols).forEach((v, i) => cols[v].colId = i); + const VT = sqlite3.vtab; + const ProtoCursor = Object.assign(Object.create(null), { row: function() { + return cache.storagePool[this.names[this.rowid]]; + } }); + Object.assign(Object.create(ProtoCursor), { + rowid: 0, + names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) + }); + const cursorState = function(cursor, reset) { + const o = cursor instanceof capi.sqlite3_vtab_cursor ? cursor : VT.xCursor.get(cursor); + if (reset || !o.vTabState) o.vTabState = Object.assign(Object.create(ProtoCursor), { + rowid: 0, + names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) + }); + return o.vTabState; + }; + const dbg = () => {}; + const theModule = function f() { + return f.mod ??= new sqlite3.capi.sqlite3_module().setupModule({ + catchExceptions: true, + methods: { + xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr) { + dbg("xConnect"); + try { + const xcol = []; + Object.keys(cols).forEach((k) => { + xcol.push(k + " " + cols[k].type); + }); + const rc = capi.sqlite3_declare_vtab(pDb, "CREATE TABLE ignored(" + xcol.join(",") + ")"); + if (0 === rc) { + const t = VT.xVtab.create(ppVtab); + util.assert(t === VT.xVtab.get(wasm.peekPtr(ppVtab)), "output pointer check failed"); + } + return rc; + } catch (e) { + return VT.xErrror("xConnect", e, capi.SQLITE_ERROR); + } + }, + xCreate: wasm.ptr.null, + xDisconnect: function(pVtab) { + dbg("xDisconnect", ...arguments); + VT.xVtab.dispose(pVtab); + return 0; + }, + xOpen: function(pVtab, ppCursor) { + dbg("xOpen", ...arguments); + VT.xCursor.create(ppCursor); + return 0; + }, + xClose: function(pCursor) { + dbg("xClose", ...arguments); + const c = VT.xCursor.unget(pCursor); + delete c.vTabState; + c.dispose(); + return 0; + }, + xNext: function(pCursor) { + dbg("xNext", ...arguments); + const c = VT.xCursor.get(pCursor); + ++cursorState(c).rowid; + return 0; + }, + xColumn: function(pCursor, pCtx, iCol) { + dbg("xColumn", ...arguments); + const st = cursorState(pCursor); + const store = st.row(); + util.assert(store, "Unexpected xColumn call"); + switch (iCol) { + case cols.rowid.colId: + capi.sqlite3_result_int(pCtx, st.rowid); + break; + case cols.name.colId: + capi.sqlite3_result_text(pCtx, store.jzClass, -1, capi.SQLITE_TRANSIENT); + break; + case cols.nRef.colId: + capi.sqlite3_result_int(pCtx, store.refc); + break; + case cols.nOpen.colId: + capi.sqlite3_result_int(pCtx, store.files.length); + break; + case cols.isTransient.colId: + capi.sqlite3_result_int(pCtx, !!store.deleteAtRefc0); + break; + case cols.dbSize.colId: + capi.sqlite3_result_int(pCtx, storageGetDbSize(store)); + break; + default: + capi.sqlite3_result_error(pCtx, "Invalid column id: " + iCol); + return capi.SQLITE_RANGE; + } + return 0; + }, + xRowid: function(pCursor, ppRowid64) { + dbg("xRowid", ...arguments); + const st = cursorState(pCursor); + VT.xRowid(ppRowid64, st.rowid); + return 0; + }, + xEof: function(pCursor) { + const st = cursorState(pCursor); + dbg("xEof?=" + !st.row(), ...arguments); + return !st.row(); + }, + xFilter: function(pCursor, idxNum, idxCStr, argc, argv) { + dbg("xFilter", ...arguments); + cursorState(pCursor, true); + return 0; + }, + xBestIndex: function(pVtab, pIdxInfo) { + dbg("xBestIndex", ...arguments); + const pii = new capi.sqlite3_index_info(pIdxInfo); + pii.$estimatedRows = cache.storagePool.size; + pii.$estimatedCost = 1; + pii.dispose(); + return 0; + } + } + }); + }; + sqlite3.kvvfs.create_module = function(pDb, name = "sqlite_kvvfs") { + return capi.sqlite3_create_module(pDb, name, theModule(), wasm.ptr.null); + }; + } + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + /** + installOpfsVfs() returns a Promise which, on success, installs an + sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs + which accept a VFS. It is intended to be called via + sqlite3ApiBootstrap.initializers or an equivalent mechanism. + + The installed VFS uses the Origin-Private FileSystem API for + all file storage. On error it is rejected with an exception + explaining the problem. Reasons for rejection include, but are + not limited to: + + - The counterpart Worker (see below) could not be loaded. + + - The environment does not support OPFS. That includes when + this function is called from the main window thread. + + Significant notes and limitations: + + - The OPFS features used here are only available in dedicated Worker + threads. This file tries to detect that case, resulting in a + rejected Promise if those features do not seem to be available. + + - It requires the SharedArrayBuffer and Atomics classes, and the + former is only available if the HTTP server emits the so-called + COOP and COEP response headers. These features are required for + proxying OPFS's synchronous API via the synchronous interface + required by the sqlite3_vfs API. + + - This function may only be called a single time. When called, this + function removes itself from the sqlite3 object. + + All arguments to this function are for internal/development purposes + only. They do not constitute a public API and may change at any + time. + + The argument may optionally be a plain object with the following + configuration options: + + - proxyUri: name of the async proxy JS file. + + - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables + logging of errors. 2 enables logging of warnings and errors. 3 + additionally enables debugging info. Logging is performed + via the sqlite3.config.{log|warn|error}() functions. + + - sanityChecks (=false): if true, some basic sanity tests are run on + the OPFS VFS API after it's initialized, before the returned + Promise resolves. This is only intended for testing and + development of the VFS, not client-side use. + + On success, the Promise resolves to the top-most sqlite3 namespace + object and that object gets a new object installed in its + `opfs` property, containing several OPFS-specific utilities. + */ + const installOpfsVfs = function callee(options) { + if (!globalThis.SharedArrayBuffer || !globalThis.Atomics) return Promise.reject(/* @__PURE__ */ new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. The server must emit the COOP/COEP response headers to enable those. See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep")); + else if ("undefined" === typeof WorkerGlobalScope) return Promise.reject(/* @__PURE__ */ new Error("The OPFS sqlite3_vfs cannot run in the main thread because it requires Atomics.wait().")); + else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); + if (!options || "object" !== typeof options) options = Object.create(null); + const urlParams = new URL(globalThis.location.href).searchParams; + if (urlParams.has("opfs-disable")) return Promise.resolve(sqlite3); + if (void 0 === options.verbose) options.verbose = urlParams.has("opfs-verbose") ? +urlParams.get("opfs-verbose") || 2 : 1; + if (void 0 === options.sanityChecks) options.sanityChecks = urlParams.has("opfs-sanity-check"); + if (void 0 === options.proxyUri) options.proxyUri = callee.defaultProxyUri; + if ("function" === typeof options.proxyUri) options.proxyUri = options.proxyUri(); + return new Promise(function(promiseResolve_, promiseReject_) { + const loggers = [ + sqlite3.config.error, + sqlite3.config.warn, + sqlite3.config.log + ]; + const logImpl = (level, ...args) => { + if (options.verbose > level) loggers[level]("OPFS syncer:", ...args); + }; + const log = (...args) => logImpl(2, ...args); + const warn = (...args) => logImpl(1, ...args); + const error = (...args) => logImpl(0, ...args); + const toss = sqlite3.util.toss; + const capi = sqlite3.capi; + const util = sqlite3.util; + const wasm = sqlite3.wasm; + const sqlite3_vfs = capi.sqlite3_vfs; + const sqlite3_file = capi.sqlite3_file; + const sqlite3_io_methods = capi.sqlite3_io_methods; + /** + Generic utilities for working with OPFS. This will get filled out + by the Promise setup and, on success, installed as sqlite3.opfs. + + ACHTUNG: do not rely on these APIs in client code. They are + experimental and subject to change or removal as the + OPFS-specific sqlite3_vfs evolves. + */ + const opfsUtil = Object.create(null); + /** + Returns true if _this_ thread has access to the OPFS APIs. + */ + const thisThreadHasOPFS = () => { + return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory; + }; + /** + Not part of the public API. Solely for internal/development + use. + */ + opfsUtil.metrics = { + dump: function() { + let k, n = 0, t = 0, w = 0; + for (k in state.opIds) { + const m = metrics[k]; + n += m.count; + t += m.time; + w += m.wait; + m.avgTime = m.count && m.time ? m.time / m.count : 0; + m.avgWait = m.count && m.wait ? m.wait / m.count : 0; + } + sqlite3.config.log(globalThis.location.href, "metrics for", globalThis.location.href, ":", metrics, "\nTotal of", n, "op(s) for", t, "ms (incl. " + w + " ms of waiting on the async side)"); + sqlite3.config.log("Serialization metrics:", metrics.s11n); + W.postMessage({ type: "opfs-async-metrics" }); + }, + reset: function() { + let k; + const r = (m) => m.count = m.time = m.wait = 0; + for (k in state.opIds) r(metrics[k] = Object.create(null)); + let s = metrics.s11n = Object.create(null); + s = s.serialize = Object.create(null); + s.count = s.time = 0; + s = metrics.s11n.deserialize = Object.create(null); + s.count = s.time = 0; + } + }; + const opfsIoMethods = new sqlite3_io_methods(); + const opfsVfs = new sqlite3_vfs().addOnDispose(() => opfsIoMethods.dispose()); + let promiseWasRejected = void 0; + const promiseReject = (err) => { + promiseWasRejected = true; + opfsVfs.dispose(); + return promiseReject_(err); + }; + const promiseResolve = () => { + promiseWasRejected = false; + return promiseResolve_(sqlite3); + }; + const W = new Worker(new URL(options.proxyUri, import.meta.url)); + setTimeout(() => { + if (void 0 === promiseWasRejected) promiseReject(/* @__PURE__ */ new Error("Timeout while waiting for OPFS async proxy worker.")); + }, 4e3); + W._originalOnError = W.onerror; + W.onerror = function(err) { + error("Error initializing OPFS asyncer:", err); + promiseReject(/* @__PURE__ */ new Error("Loading OPFS async Worker failed for unknown reasons.")); + }; + const pDVfs = capi.sqlite3_vfs_find(null); + const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : null; + opfsIoMethods.$iVersion = 1; + opfsVfs.$iVersion = 2; + opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + opfsVfs.$mxPathname = 1024; + opfsVfs.$zName = wasm.allocCString("opfs"); + opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; + opfsVfs.addOnDispose("$zName", opfsVfs.$zName, "cleanup default VFS wrapper", () => dVfs ? dVfs.dispose() : null); + /** + Pedantic sidebar about opfsVfs.ondispose: the entries in that array + are items to clean up when opfsVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown order + of the wasm engine and the JS one are undefined so there is no + guaranty that the opfsVfs instance would be available in one + environment or the other when sqlite3_os_end() is called (_if_ it + gets called at all in a wasm build, which is undefined). + */ + /** + State which we send to the async-api Worker or share with it. + This object must initially contain only cloneable or sharable + objects. After the worker's "inited" message arrives, other types + of data may be added to it. + + For purposes of Atomics.wait() and Atomics.notify(), we use a + SharedArrayBuffer with one slot reserved for each of the API + proxy's methods. The sync side of the API uses Atomics.wait() + on the corresponding slot and the async side uses + Atomics.notify() on that slot. + + The approach of using a single SAB to serialize comms for all + instances might(?) lead to deadlock situations in multi-db + cases. We should probably have one SAB here with a single slot + for locking a per-file initialization step and then allocate a + separate SAB like the above one for each file. That will + require a bit of acrobatics but should be feasible. The most + problematic part is that xOpen() would have to use + postMessage() to communicate its SharedArrayBuffer, and mixing + that approach with Atomics.wait/notify() gets a bit messy. + */ + const state = Object.create(null); + state.verbose = options.verbose; + state.littleEndian = (() => { + const buffer = /* @__PURE__ */ new ArrayBuffer(2); + new DataView(buffer).setInt16(0, 256, true); + return new Int16Array(buffer)[0] === 256; + })(); + /** + asyncIdleWaitTime is how long (ms) to wait, in the async proxy, + for each Atomics.wait() when waiting on inbound VFS API calls. + We need to wake up periodically to give the thread a chance to + do other things. If this is too high (e.g. 500ms) then even two + workers/tabs can easily run into locking errors. Some multiple + of this value is also used for determining how long to wait on + lock contention to free up. + */ + state.asyncIdleWaitTime = 150; + /** + Whether the async counterpart should log exceptions to + the serialization channel. That produces a great deal of + noise for seemingly innocuous things like xAccess() checks + for missing files, so this option may have one of 3 values: + + 0 = no exception logging. + + 1 = only log exceptions for "significant" ops like xOpen(), + xRead(), and xWrite(). + + 2 = log all exceptions. + */ + state.asyncS11nExceptions = 1; + state.fileBufferSize = 1024 * 64; + state.sabS11nOffset = state.fileBufferSize; + /** + The size of the block in our SAB for serializing arguments and + result values. Needs to be large enough to hold serialized + values of any of the proxied APIs. Filenames are the largest + part but are limited to opfsVfs.$mxPathname bytes. We also + store exceptions there, so it needs to be long enough to hold + a reasonably long exception string. + */ + state.sabS11nSize = opfsVfs.$mxPathname * 2; + /** + The SAB used for all data I/O between the synchronous and + async halves (file i/o and arg/result s11n). + */ + state.sabIO = new SharedArrayBuffer(state.fileBufferSize + state.sabS11nSize); + state.opIds = Object.create(null); + const metrics = Object.create(null); + { + let i = 0; + state.opIds.whichOp = i++; + state.opIds.rc = i++; + state.opIds.xAccess = i++; + state.opIds.xClose = i++; + state.opIds.xDelete = i++; + state.opIds.xDeleteNoWait = i++; + state.opIds.xFileSize = i++; + state.opIds.xLock = i++; + state.opIds.xOpen = i++; + state.opIds.xRead = i++; + state.opIds.xSleep = i++; + state.opIds.xSync = i++; + state.opIds.xTruncate = i++; + state.opIds.xUnlock = i++; + state.opIds.xWrite = i++; + state.opIds.mkdir = i++; + state.opIds["opfs-async-metrics"] = i++; + state.opIds["opfs-async-shutdown"] = i++; + state.opIds.retry = i++; + state.sabOP = new SharedArrayBuffer(i * 4); + opfsUtil.metrics.reset(); + } + /** + SQLITE_xxx constants to export to the async worker + counterpart... + */ + state.sq3Codes = Object.create(null); + [ + "SQLITE_ACCESS_EXISTS", + "SQLITE_ACCESS_READWRITE", + "SQLITE_BUSY", + "SQLITE_CANTOPEN", + "SQLITE_ERROR", + "SQLITE_IOERR", + "SQLITE_IOERR_ACCESS", + "SQLITE_IOERR_CLOSE", + "SQLITE_IOERR_DELETE", + "SQLITE_IOERR_FSYNC", + "SQLITE_IOERR_LOCK", + "SQLITE_IOERR_READ", + "SQLITE_IOERR_SHORT_READ", + "SQLITE_IOERR_TRUNCATE", + "SQLITE_IOERR_UNLOCK", + "SQLITE_IOERR_WRITE", + "SQLITE_LOCK_EXCLUSIVE", + "SQLITE_LOCK_NONE", + "SQLITE_LOCK_PENDING", + "SQLITE_LOCK_RESERVED", + "SQLITE_LOCK_SHARED", + "SQLITE_LOCKED", + "SQLITE_MISUSE", + "SQLITE_NOTFOUND", + "SQLITE_OPEN_CREATE", + "SQLITE_OPEN_DELETEONCLOSE", + "SQLITE_OPEN_MAIN_DB", + "SQLITE_OPEN_READONLY" + ].forEach((k) => { + if (void 0 === (state.sq3Codes[k] = capi[k])) toss("Maintenance required: not found:", k); + }); + state.opfsFlags = Object.assign(Object.create(null), { + OPFS_UNLOCK_ASAP: 1, + OPFS_UNLINK_BEFORE_OPEN: 2, + defaultUnlockAsap: false + }); + /** + Runs the given operation (by name) in the async worker + counterpart, waits for its response, and returns the result + which the async worker writes to SAB[state.opIds.rc]. The + 2nd and subsequent arguments must be the arguments for the + async op. + */ + const opRun = (op, ...args) => { + const opNdx = state.opIds[op] || toss("Invalid op ID:", op); + state.s11n.serialize(...args); + Atomics.store(state.sabOPView, state.opIds.rc, -1); + Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); + Atomics.notify(state.sabOPView, state.opIds.whichOp); + const t = performance.now(); + while ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.rc, -1)); + const rc = Atomics.load(state.sabOPView, state.opIds.rc); + metrics[op].wait += performance.now() - t; + if (rc && state.asyncS11nExceptions) { + const err = state.s11n.deserialize(); + if (err) error(op + "() async error:", ...err); + } + return rc; + }; + /** + Not part of the public API. Only for test/development use. + */ + opfsUtil.debug = { + asyncShutdown: () => { + warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); + opRun("opfs-async-shutdown"); + }, + asyncRestart: () => { + warn("Attempting to restart OPFS VFS async listener. Might work, might not."); + W.postMessage({ type: "opfs-async-restart" }); + } + }; + const initS11n = () => { + /** + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ACHTUNG: this code is 100% duplicated in the other half of + this proxy! The documentation is maintained in the + "synchronous half". + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + This proxy de/serializes cross-thread function arguments and + output-pointer values via the state.sabIO SharedArrayBuffer, + using the region defined by (state.sabS11nOffset, + state.sabS11nOffset + state.sabS11nSize]. Only one dataset is + recorded at a time. + + This is not a general-purpose format. It only supports the + range of operations, and data sizes, needed by the + sqlite3_vfs and sqlite3_io_methods operations. Serialized + data are transient and this serialization algorithm may + change at any time. + + The data format can be succinctly summarized as: + + Nt...Td...D + + Where: + + - N = number of entries (1 byte) + + - t = type ID of first argument (1 byte) + + - ...T = type IDs of the 2nd and subsequent arguments (1 byte + each). + + - d = raw bytes of first argument (per-type size). + + - ...D = raw bytes of the 2nd and subsequent arguments (per-type + size). + + All types except strings have fixed sizes. Strings are stored + using their TextEncoder/TextDecoder representations. It would + arguably make more sense to store them as Int16Arrays of + their JS character values, but how best/fastest to get that + in and out of string form is an open point. Initial + experimentation with that approach did not gain us any speed. + + Historical note: this impl was initially about 1% this size by + using using JSON.stringify/parse(), but using fit-to-purpose + serialization saves considerable runtime. + */ + if (state.s11n) return state.s11n; + const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + state.s11n = Object.create(null); + const TypeIds = Object.create(null); + TypeIds.number = { + id: 1, + size: 8, + getter: "getFloat64", + setter: "setFloat64" + }; + TypeIds.bigint = { + id: 2, + size: 8, + getter: "getBigInt64", + setter: "setBigInt64" + }; + TypeIds.boolean = { + id: 3, + size: 4, + getter: "getInt32", + setter: "setInt32" + }; + TypeIds.string = { id: 4 }; + const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v); + const getTypeIdById = (tid) => { + switch (tid) { + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:", tid); + } + }; + /** + Returns an array of the deserialized state stored by the most + recent serialize() operation (from this thread or the + counterpart thread), or null if the serialization buffer is + empty. If passed a truthy argument, the serialization buffer + is cleared after deserialization. + */ + state.s11n.deserialize = function(clear = false) { + ++metrics.s11n.deserialize.count; + const t = performance.now(); + const argc = viewU8[0]; + const rc = argc ? [] : null; + if (argc) { + const typeIds = []; + let offset = 1, i, n, v; + for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset])); + for (i = 0; i < argc; ++i) { + const t = typeIds[i]; + if (t.getter) { + v = viewDV[t.getter](offset, state.littleEndian); + offset += t.size; + } else { + n = viewDV.getInt32(offset, state.littleEndian); + offset += 4; + v = textDecoder.decode(viewU8.slice(offset, offset + n)); + offset += n; + } + rc.push(v); + } + } + if (clear) viewU8[0] = 0; + metrics.s11n.deserialize.time += performance.now() - t; + return rc; + }; + /** + Serializes all arguments to the shared buffer for consumption + by the counterpart thread. + + This routine is only intended for serializing OPFS VFS + arguments and (in at least one special case) result values, + and the buffer is sized to be able to comfortably handle + those. + + If passed no arguments then it zeroes out the serialization + state. + */ + state.s11n.serialize = function(...args) { + const t = performance.now(); + ++metrics.s11n.serialize.count; + if (args.length) { + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 255; + for (; i < args.length; ++i, ++offset) { + typeIds.push(getTypeId(args[i])); + viewU8[offset] = typeIds[i].id; + } + for (i = 0; i < args.length; ++i) { + const t = typeIds[i]; + if (t.setter) { + viewDV[t.setter](offset, args[i], state.littleEndian); + offset += t.size; + } else { + const s = textEncoder.encode(args[i]); + viewDV.setInt32(offset, s.byteLength, state.littleEndian); + offset += 4; + viewU8.set(s, offset); + offset += s.byteLength; + } + } + } else viewU8[0] = 0; + metrics.s11n.serialize.time += performance.now() - t; + }; + return state.s11n; + }; + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + const randomFilename = function f(len = 16) { + if (!f._chars) { + f._chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for (; i < len; ++i) { + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(""); + }; + /** + Map of sqlite3_file pointers to objects constructed by xOpen(). + */ + const __openFiles = Object.create(null); + const opTimer = Object.create(null); + opTimer.op = void 0; + opTimer.start = void 0; + const mTimeStart = (op) => { + opTimer.start = performance.now(); + opTimer.op = op; + ++metrics[op].count; + }; + const mTimeEnd = () => metrics[opTimer.op].time += performance.now() - opTimer.start; + /** + Impls for the sqlite3_io_methods methods. Maintenance reminder: + members are in alphabetical order to simplify finding them. + */ + const ioSyncWrappers = { + xCheckReservedLock: function(pFile, pOut) { + /** + As of late 2022, only a single lock can be held on an OPFS + file. We have no way of checking whether any _other_ db + connection has a lock except by trying to obtain and (on + success) release a sync-handle for it, but doing so would + involve an inherent race condition. For the time being, + pending a better solution, we simply report whether the + given pFile is open. + + Update 2024-06-12: based on forum discussions, this + function now always sets pOut to 0 (false): + + https://sqlite.org/forum/forumpost/a2f573b00cda1372 + */ + wasm.poke(pOut, 0, "i32"); + return 0; + }, + xClose: function(pFile) { + mTimeStart("xClose"); + let rc = 0; + const f = __openFiles[pFile]; + if (f) { + delete __openFiles[pFile]; + rc = opRun("xClose", pFile); + if (f.sq3File) f.sq3File.dispose(); + } + mTimeEnd(); + return rc; + }, + xDeviceCharacteristics: function(pFile) { + return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + }, + xFileControl: function(pFile, opId, pArg) { + return capi.SQLITE_NOTFOUND; + }, + xFileSize: function(pFile, pSz64) { + mTimeStart("xFileSize"); + let rc = opRun("xFileSize", pFile); + if (0 == rc) try { + const sz = state.s11n.deserialize()[0]; + wasm.poke(pSz64, sz, "i64"); + } catch (e) { + error("Unexpected error reading xFileSize() result:", e); + rc = state.sq3Codes.SQLITE_IOERR; + } + mTimeEnd(); + return rc; + }, + xLock: function(pFile, lockType) { + mTimeStart("xLock"); + const f = __openFiles[pFile]; + let rc = 0; + if (!f.lockType) { + rc = opRun("xLock", pFile, lockType); + if (0 === rc) f.lockType = lockType; + } else f.lockType = lockType; + mTimeEnd(); + return rc; + }, + xRead: function(pFile, pDest, n, offset64) { + mTimeStart("xRead"); + const f = __openFiles[pFile]; + let rc; + try { + rc = opRun("xRead", pFile, n, Number(offset64)); + if (0 === rc || capi.SQLITE_IOERR_SHORT_READ === rc) + /** + Results get written to the SharedArrayBuffer f.sabView. + Because the heap is _not_ a SharedArrayBuffer, we have + to copy the results. TypedArray.set() seems to be the + fastest way to copy this. */ + wasm.heap8u().set(f.sabView.subarray(0, n), Number(pDest)); + } catch (e) { + error("xRead(", arguments, ") failed:", e, f); + rc = capi.SQLITE_IOERR_READ; + } + mTimeEnd(); + return rc; + }, + xSync: function(pFile, flags) { + mTimeStart("xSync"); + ++metrics.xSync.count; + const rc = opRun("xSync", pFile, flags); + mTimeEnd(); + return rc; + }, + xTruncate: function(pFile, sz64) { + mTimeStart("xTruncate"); + const rc = opRun("xTruncate", pFile, Number(sz64)); + mTimeEnd(); + return rc; + }, + xUnlock: function(pFile, lockType) { + mTimeStart("xUnlock"); + const f = __openFiles[pFile]; + let rc = 0; + if (capi.SQLITE_LOCK_NONE === lockType && f.lockType) rc = opRun("xUnlock", pFile, lockType); + if (0 === rc) f.lockType = lockType; + mTimeEnd(); + return rc; + }, + xWrite: function(pFile, pSrc, n, offset64) { + mTimeStart("xWrite"); + const f = __openFiles[pFile]; + let rc; + try { + f.sabView.set(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n)); + rc = opRun("xWrite", pFile, n, Number(offset64)); + } catch (e) { + error("xWrite(", arguments, ") failed:", e, f); + rc = capi.SQLITE_IOERR_WRITE; + } + mTimeEnd(); + return rc; + } + }; + /** + Impls for the sqlite3_vfs methods. Maintenance reminder: members + are in alphabetical order to simplify finding them. + */ + const vfsSyncWrappers = { + xAccess: function(pVfs, zName, flags, pOut) { + mTimeStart("xAccess"); + const rc = opRun("xAccess", wasm.cstrToJs(zName)); + wasm.poke(pOut, rc ? 0 : 1, "i32"); + mTimeEnd(); + return 0; + }, + xCurrentTime: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); + return 0; + }, + xCurrentTimeInt64: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); + return 0; + }, + xDelete: function(pVfs, zName, doSyncDir) { + mTimeStart("xDelete"); + const rc = opRun("xDelete", wasm.cstrToJs(zName), doSyncDir, false); + mTimeEnd(); + return rc; + }, + xFullPathname: function(pVfs, zName, nOut, pOut) { + return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; + }, + xGetLastError: function(pVfs, nOut, pOut) { + warn("OPFS xGetLastError() has nothing sensible to return."); + return 0; + }, + xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { + mTimeStart("xOpen"); + let opfsFlags = 0; + if (0 === zName) zName = randomFilename(); + else if (wasm.isPtr(zName)) { + if (capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP; + if (capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; + zName = wasm.cstrToJs(zName); + } + const fh = Object.create(null); + fh.fid = pFile; + fh.filename = zName; + fh.sab = new SharedArrayBuffer(state.fileBufferSize); + fh.flags = flags; + fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) && !!(flags & capi.SQLITE_OPEN_READONLY); + const rc = opRun("xOpen", pFile, zName, flags, opfsFlags); + if (!rc) { + if (fh.readOnly) wasm.poke(pOutFlags, capi.SQLITE_OPEN_READONLY, "i32"); + __openFiles[pFile] = fh; + fh.sabView = state.sabFileBufView; + fh.sq3File = new sqlite3_file(pFile); + fh.sq3File.$pMethods = opfsIoMethods.pointer; + fh.lockType = capi.SQLITE_LOCK_NONE; + } + mTimeEnd(); + return rc; + } + }; + if (dVfs) { + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + } + if (!opfsVfs.$xRandomness) vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut) { + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; + return i; + }; + if (!opfsVfs.$xSleep) vfsSyncWrappers.xSleep = function(pVfs, ms) { + Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); + return 0; + }; + /** + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd arg + is true, the result is returned as an array of path elements, + else an absolute path string is returned. + */ + opfsUtil.getResolvedPath = function(filename, splitIt) { + const p = new URL(filename, "file://irrelevant").pathname; + return splitIt ? p.split("/").filter((v) => !!v) : p; + }; + /** + Takes the absolute path to a filesystem element. Returns an + array of [handleOfContainingDir, filename]. If the 2nd argument + is truthy then each directory element leading to the file is + created along the way. Throws if any creation or resolution + fails. + */ + opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false) { + const path = opfsUtil.getResolvedPath(absFilename, true); + const filename = path.pop(); + let dh = opfsUtil.rootDirectory; + for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); + return [dh, filename]; + }; + /** + Creates the given directory name, recursively, in + the OPFS filesystem. Returns true if it succeeds or the + directory already exists, else false. + */ + opfsUtil.mkdir = async function(absDirName) { + try { + await opfsUtil.getDirForFilename(absDirName + "/filepart", true); + return true; + } catch (e) { + return false; + } + }; + /** + Checks whether the given OPFS filesystem entry exists, + returning true if it does, false if it doesn't or if an + exception is intercepted while trying to make the + determination. + */ + opfsUtil.entryExists = async function(fsEntryName) { + try { + const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); + await dh.getFileHandle(fn); + return true; + } catch (e) { + return false; + } + }; + /** + Generates a random ASCII string, intended for use as a + temporary file name. Its argument is the length of the string, + defaulting to 16. + */ + opfsUtil.randomFilename = randomFilename; + /** + Returns a promise which resolves to an object which represents + all files and directories in the OPFS tree. The top-most object + has two properties: `dirs` is an array of directory entries + (described below) and `files` is a list of file names for all + files in that directory. + + Traversal starts at sqlite3.opfs.rootDirectory. + + Each `dirs` entry is an object in this form: + + ``` + { name: directoryName, + dirs: [...subdirs], + files: [...file names] + } + ``` + + The `files` and `subdirs` entries are always set but may be + empty arrays. + + The returned object has the same structure but its `name` is + an empty string. All returned objects are created with + Object.create(null), so have no prototype. + + Design note: the entries do not contain more information, + e.g. file sizes, because getting such info is not only + expensive but is subject to locking-related errors. + */ + opfsUtil.treeList = async function() { + const doDir = async function callee(dirHandle, tgt) { + tgt.name = dirHandle.name; + tgt.dirs = []; + tgt.files = []; + for await (const handle of dirHandle.values()) if ("directory" === handle.kind) { + const subDir = Object.create(null); + tgt.dirs.push(subDir); + await callee(handle, subDir); + } else tgt.files.push(handle.name); + }; + const root = Object.create(null); + await doDir(opfsUtil.rootDirectory, root); + return root; + }; + /** + Irrevocably deletes _all_ files in the current origin's OPFS. + Obviously, this must be used with great caution. It may throw + an exception if removal of anything fails (e.g. a file is + locked), but the precise conditions under which the underlying + APIs will throw are not documented (so we cannot tell you what + they are). + */ + opfsUtil.rmfr = async function() { + const dir = opfsUtil.rootDirectory, opt = { recurse: true }; + for await (const handle of dir.values()) dir.removeEntry(handle.name, opt); + }; + /** + Deletes the given OPFS filesystem entry. As this environment + has no notion of "current directory", the given name must be an + absolute path. If the 2nd argument is truthy, deletion is + recursive (use with caution!). + + The returned Promise resolves to true if the deletion was + successful, else false (but...). The OPFS API reports the + reason for the failure only in human-readable form, not + exceptions which can be type-checked to determine the + failure. Because of that... + + If the final argument is truthy then this function will + propagate any exception on error, rather than returning false. + */ + opfsUtil.unlink = async function(fsEntryName, recursive = false, throwOnError = false) { + try { + const [hDir, filenamePart] = await opfsUtil.getDirForFilename(fsEntryName, false); + await hDir.removeEntry(filenamePart, { recursive }); + return true; + } catch (e) { + if (throwOnError) throw new Error("unlink(", arguments[0], ") failed: " + e.message, { cause: e }); + return false; + } + }; + /** + Traverses the OPFS filesystem, calling a callback for each + entry. The argument may be either a callback function or an + options object with any of the following properties: + + - `callback`: function which gets called for each filesystem + entry. It gets passed 3 arguments: 1) the + FileSystemFileHandle or FileSystemDirectoryHandle of each + entry (noting that both are instanceof FileSystemHandle). 2) + the FileSystemDirectoryHandle of the parent directory. 3) the + current depth level, with 0 being at the top of the tree + relative to the starting directory. If the callback returns a + literal false, as opposed to any other falsy value, traversal + stops without an error. Any exceptions it throws are + propagated. Results are undefined if the callback manipulate + the filesystem (e.g. removing or adding entries) because the + how OPFS iterators behave in the face of such changes is + undocumented. + + - `recursive` [bool=true]: specifies whether to recurse into + subdirectories or not. Whether recursion is depth-first or + breadth-first is unspecified! + + - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] + specifies the starting directory. + + If this function is passed a function, it is assumed to be the + callback. + + Returns a promise because it has to (by virtue of being async) + but that promise has no specific meaning: the traversal it + performs is synchronous. The promise must be used to catch any + exceptions propagated by the callback, however. + */ + opfsUtil.traverse = async function(opt) { + const defaultOpt = { + recursive: true, + directory: opfsUtil.rootDirectory + }; + if ("function" === typeof opt) opt = { callback: opt }; + opt = Object.assign(defaultOpt, opt || {}); + (async function callee(dirHandle, depth) { + for await (const handle of dirHandle.values()) if (false === opt.callback(handle, dirHandle, depth)) return false; + else if (opt.recursive && "directory" === handle.kind) { + if (false === await callee(handle, depth + 1)) break; + } + })(opt.directory, 0); + }; + /** + impl of importDb() when it's given a function as its second + argument. + */ + const importDbChunked = async function(filename, callback) { + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + let sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(), nWrote = 0, chunk, checkedHeader = false; + try { + sah.truncate(0); + while (void 0 !== (chunk = await callback())) { + if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, { at: nWrote }); + nWrote += chunk.byteLength; + } + if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); + if (!checkedHeader) { + const header = new Uint8Array(20); + sah.read(header, { at: 0 }); + util.affirmDbHeader(header); + } + sah.write(new Uint8Array([1, 1]), { at: 18 }); + return nWrote; + } catch (e) { + await sah.close(); + sah = void 0; + await hDir.removeEntry(fnamePart).catch(() => {}); + throw e; + } finally { + if (sah) await sah.close(); + } + }; + /** + Asynchronously imports the given bytes (a byte array or + ArrayBuffer) into the given database file. + + Results are undefined if the given db name refers to an opened + db. + + If passed a function for its second argument, its behaviour + changes: imports its data in chunks fed to it by the given + callback function. It calls the callback (which may be async) + repeatedly, expecting either a Uint8Array or ArrayBuffer (to + denote new input) or undefined (to denote EOF). For so long as + the callback continues to return non-undefined, it will append + incoming data to the given VFS-hosted database file. When + called this way, the resolved value of the returned Promise is + the number of bytes written to the target file. + + It very specifically requires the input to be an SQLite3 + database and throws if that's not the case. It does so in + order to prevent this function from taking on a larger scope + than it is specifically intended to. i.e. we do not want it to + become a convenience for importing arbitrary files into OPFS. + + This routine rewrites the database header bytes in the output + file (not the input array) to force disabling of WAL mode. + + On error this throws and the state of the input file is + undefined (it depends on where the exception was triggered). + + On success, resolves to the number of bytes written. + */ + opfsUtil.importDb = async function(filename, bytes) { + if (bytes instanceof Function) return importDbChunked(filename, bytes); + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + util.affirmIsDb(bytes); + const n = bytes.byteLength; + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + let sah, nWrote = 0; + try { + sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(); + sah.truncate(0); + nWrote = sah.write(bytes, { at: 0 }); + if (nWrote != n) toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); + sah.write(new Uint8Array([1, 1]), { at: 18 }); + return nWrote; + } catch (e) { + if (sah) { + await sah.close(); + sah = void 0; + } + await hDir.removeEntry(fnamePart).catch(() => {}); + throw e; + } finally { + if (sah) await sah.close(); + } + }; + if (sqlite3.oo1) { + const OpfsDb = function(...args) { + const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); + opt.vfs = opfsVfs.$zName; + sqlite3.oo1.DB.dbCtorHelper.call(this, opt); + }; + OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); + sqlite3.oo1.OpfsDb = OpfsDb; + OpfsDb.importDb = opfsUtil.importDb; + sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback(opfsVfs.pointer, function(oo1Db, sqlite3) { + sqlite3.capi.sqlite3_busy_timeout(oo1Db, 1e4); + }); + } + const sanityCheck = function() { + const scope = wasm.scopedAllocPush(); + const sq3File = new sqlite3_file(); + try { + const fid = sq3File.pointer; + const openFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE | capi.SQLITE_OPEN_MAIN_DB; + const pOut = wasm.scopedAlloc(8); + const dbFile = "/sanity/check/file" + randomFilename(8); + const zDbFile = wasm.scopedAllocCString(dbFile); + let rc; + state.s11n.serialize("This is ä string."); + rc = state.s11n.deserialize(); + log("deserialize() says:", rc); + if ("This is ä string." !== rc[0]) toss("String d13n error."); + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut, "i32"); + log("xAccess(", dbFile, ") exists ?=", rc); + rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, fid, openFlags, pOut); + log("open rc =", rc, "state.sabOPView[xOpen] =", state.sabOPView[state.opIds.xOpen]); + if (0 !== rc) { + error("open failed with code", rc); + return; + } + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut, "i32"); + if (!rc) toss("xAccess() failed to detect file."); + rc = ioSyncWrappers.xSync(sq3File.pointer, 0); + if (rc) toss("sync failed w/ rc", rc); + rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); + if (rc) toss("truncate failed w/ rc", rc); + wasm.poke(pOut, 0, "i64"); + rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); + if (rc) toss("xFileSize failed w/ rc", rc); + log("xFileSize says:", wasm.peek(pOut, "i64")); + rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); + if (rc) toss("xWrite() failed!"); + const readBuf = wasm.scopedAlloc(16); + rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); + wasm.poke(readBuf + 6, 0); + let jRead = wasm.cstrToJs(readBuf); + log("xRead() got:", jRead); + if ("sanity" !== jRead) toss("Unexpected xRead() value."); + if (vfsSyncWrappers.xSleep) { + log("xSleep()ing before close()ing..."); + vfsSyncWrappers.xSleep(opfsVfs.pointer, 2e3); + log("waking up from xSleep()"); + } + rc = ioSyncWrappers.xClose(fid); + log("xClose rc =", rc, "sabOPView =", state.sabOPView); + log("Deleting file:", dbFile); + vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 4660); + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut, "i32"); + if (rc) toss("Expecting 0 from xAccess(", dbFile, ") after xDelete()."); + warn("End of OPFS sanity checks."); + } finally { + sq3File.dispose(); + wasm.scopedAllocPop(scope); + } + }; + W.onmessage = function({ data }) { + switch (data.type) { + case "opfs-unavailable": + promiseReject(new Error(data.payload.join(" "))); + break; + case "opfs-async-loaded": + W.postMessage({ + type: "opfs-async-init", + args: state + }); + break; + case "opfs-async-inited": + if (true === promiseWasRejected) break; + try { + sqlite3.vfs.installVfs({ + io: { + struct: opfsIoMethods, + methods: ioSyncWrappers + }, + vfs: { + struct: opfsVfs, + methods: vfsSyncWrappers + } + }); + state.sabOPView = new Int32Array(state.sabOP); + state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); + state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + initS11n(); + if (options.sanityChecks) { + warn("Running sanity checks because of opfs-sanity-check URL arg..."); + sanityCheck(); + } + if (thisThreadHasOPFS()) navigator.storage.getDirectory().then((d) => { + W.onerror = W._originalOnError; + delete W._originalOnError; + sqlite3.opfs = opfsUtil; + opfsUtil.rootDirectory = d; + log("End of OPFS sqlite3_vfs setup.", opfsVfs); + promiseResolve(); + }).catch(promiseReject); + else promiseResolve(); + } catch (e) { + error(e); + promiseReject(e); + } + break; + default: { + const errMsg = "Unexpected message from the OPFS async worker: " + JSON.stringify(data); + error(errMsg); + promiseReject(new Error(errMsg)); + break; + } + } + }; + }); + }; + installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js"; + globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3) => { + try { + let proxyJs = installOpfsVfs.defaultProxyUri; + if (sqlite3?.scriptInfo?.sqlite3Dir) installOpfsVfs.defaultProxyUri = sqlite3.scriptInfo.sqlite3Dir + proxyJs; + return installOpfsVfs().catch((e) => { + sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:", e.message); + }); + } catch (e) { + sqlite3.config.error("installOpfsVfs() exception:", e); + return Promise.reject(e); + } + }); + }); + globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { + "use strict"; + const toss = sqlite3.util.toss; + const toss3 = sqlite3.util.toss3; + const initPromises = Object.create(null); + const capi = sqlite3.capi; + const util = sqlite3.util; + const wasm = sqlite3.wasm; + const SECTOR_SIZE = 4096; + const HEADER_MAX_PATH_SIZE = 512; + const HEADER_FLAGS_SIZE = 4; + const HEADER_DIGEST_SIZE = 8; + const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE; + const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE; + const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE; + const HEADER_OFFSET_DATA = SECTOR_SIZE; + const PERSISTENT_FILE_TYPES = capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_MAIN_JOURNAL | capi.SQLITE_OPEN_SUPER_JOURNAL | capi.SQLITE_OPEN_WAL; + const FLAG_COMPUTE_DIGEST_V2 = capi.SQLITE_OPEN_MEMORY; + /** Subdirectory of the VFS's space where "opaque" (randomly-named) + files are stored. Changing this effectively invalidates the data + stored under older names (orphaning it), so don't do that. */ + const OPAQUE_DIR_NAME = ".opaque"; + /** + Returns short a string of random alphanumeric characters + suitable for use as a random filename. + */ + const getRandomName = () => Math.random().toString(36).slice(2); + const textDecoder = new TextDecoder(); + const textEncoder = new TextEncoder(); + const optionDefaults = Object.assign(Object.create(null), { + name: "opfs-sahpool", + directory: void 0, + initialCapacity: 6, + clearOnInit: false, + verbosity: 2, + forceReinitIfPreviouslyFailed: false + }); + /** Logging routines, from most to least serious. */ + const loggers = [ + sqlite3.config.error, + sqlite3.config.warn, + sqlite3.config.log + ]; + sqlite3.config.log; + const warn = sqlite3.config.warn; + sqlite3.config.error; + const __mapVfsToPool = /* @__PURE__ */ new Map(); + const getPoolForVfs = (pVfs) => __mapVfsToPool.get(pVfs); + const setPoolForVfs = (pVfs, pool) => { + if (pool) __mapVfsToPool.set(pVfs, pool); + else __mapVfsToPool.delete(pVfs); + }; + const __mapSqlite3File = /* @__PURE__ */ new Map(); + const getPoolForPFile = (pFile) => __mapSqlite3File.get(pFile); + const setPoolForPFile = (pFile, pool) => { + if (pool) __mapSqlite3File.set(pFile, pool); + else __mapSqlite3File.delete(pFile); + }; + /** + Impls for the sqlite3_io_methods methods. Maintenance reminder: + members are in alphabetical order to simplify finding them. + */ + const ioMethods = { + xCheckReservedLock: function(pFile, pOut) { + const pool = getPoolForPFile(pFile); + pool.log("xCheckReservedLock"); + pool.storeErr(); + wasm.poke32(pOut, 1); + return 0; + }, + xClose: function(pFile) { + const pool = getPoolForPFile(pFile); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + if (file) try { + pool.log(`xClose ${file.path}`); + pool.mapS3FileToOFile(pFile, false); + file.sah.flush(); + if (file.flags & capi.SQLITE_OPEN_DELETEONCLOSE) pool.deletePath(file.path); + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + return 0; + }, + xDeviceCharacteristics: function(pFile) { + return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + }, + xFileControl: function(pFile, opId, pArg) { + return capi.SQLITE_NOTFOUND; + }, + xFileSize: function(pFile, pSz64) { + const pool = getPoolForPFile(pFile); + pool.log(`xFileSize`); + const size = pool.getOFileForS3File(pFile).sah.getSize() - HEADER_OFFSET_DATA; + wasm.poke64(pSz64, BigInt(size)); + return 0; + }, + xLock: function(pFile, lockType) { + const pool = getPoolForPFile(pFile); + pool.log(`xLock ${lockType}`); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + file.lockType = lockType; + return 0; + }, + xRead: function(pFile, pDest, n, offset64) { + const pool = getPoolForPFile(pFile); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + pool.log(`xRead ${file.path} ${n} @ ${offset64}`); + try { + const nRead = file.sah.read(wasm.heap8u().subarray(Number(pDest), Number(pDest) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }); + if (nRead < n) { + wasm.heap8u().fill(0, Number(pDest) + nRead, Number(pDest) + n); + return capi.SQLITE_IOERR_SHORT_READ; + } + return 0; + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + }, + xSectorSize: function(pFile) { + return SECTOR_SIZE; + }, + xSync: function(pFile, flags) { + const pool = getPoolForPFile(pFile); + pool.log(`xSync ${flags}`); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + try { + file.sah.flush(); + return 0; + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + }, + xTruncate: function(pFile, sz64) { + const pool = getPoolForPFile(pFile); + pool.log(`xTruncate ${sz64}`); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + try { + file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64)); + return 0; + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + }, + xUnlock: function(pFile, lockType) { + const pool = getPoolForPFile(pFile); + pool.log("xUnlock"); + const file = pool.getOFileForS3File(pFile); + file.lockType = lockType; + return 0; + }, + xWrite: function(pFile, pSrc, n, offset64) { + const pool = getPoolForPFile(pFile); + pool.storeErr(); + const file = pool.getOFileForS3File(pFile); + pool.log(`xWrite ${file.path} ${n} ${offset64}`); + try { + return n === file.sah.write(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }) ? 0 : toss("Unknown write() failure."); + } catch (e) { + return pool.storeErr(e, capi.SQLITE_IOERR); + } + } + }; + const opfsIoMethods = new capi.sqlite3_io_methods(); + opfsIoMethods.$iVersion = 1; + sqlite3.vfs.installVfs({ io: { + struct: opfsIoMethods, + methods: ioMethods + } }); + /** + Impls for the sqlite3_vfs methods. Maintenance reminder: members + are in alphabetical order to simplify finding them. + */ + const vfsMethods = { + xAccess: function(pVfs, zName, flags, pOut) { + const pool = getPoolForVfs(pVfs); + pool.storeErr(); + try { + const name = pool.getPath(zName); + wasm.poke32(pOut, pool.hasFilename(name) ? 1 : 0); + } catch (e) { + wasm.poke32(pOut, 0); + } + return 0; + }, + xCurrentTime: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); + return 0; + }, + xCurrentTimeInt64: function(pVfs, pOut) { + wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); + return 0; + }, + xDelete: function(pVfs, zName, doSyncDir) { + const pool = getPoolForVfs(pVfs); + pool.log(`xDelete ${wasm.cstrToJs(zName)}`); + pool.storeErr(); + try { + pool.deletePath(pool.getPath(zName)); + return 0; + } catch (e) { + pool.storeErr(e); + return capi.SQLITE_IOERR_DELETE; + } + }, + xFullPathname: function(pVfs, zName, nOut, pOut) { + return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; + }, + xGetLastError: function(pVfs, nOut, pOut) { + const pool = getPoolForVfs(pVfs); + const e = pool.popErr(); + pool.log(`xGetLastError ${nOut} e =`, e); + if (e) { + const scope = wasm.scopedAllocPush(); + try { + const [cMsg, n] = wasm.scopedAllocCString(e.message, true); + wasm.cstrncpy(pOut, cMsg, nOut); + if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); + } catch (e) { + return capi.SQLITE_NOMEM; + } finally { + wasm.scopedAllocPop(scope); + } + } + return e ? e.sqlite3Rc || capi.SQLITE_IOERR : 0; + }, + xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { + const pool = getPoolForVfs(pVfs); + try { + flags &= ~FLAG_COMPUTE_DIGEST_V2; + pool.log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`); + const path = zName && wasm.peek8(zName) ? pool.getPath(zName) : getRandomName(); + let sah = pool.getSAHForPath(path); + if (!sah && flags & capi.SQLITE_OPEN_CREATE) if (pool.getFileCount() < pool.getCapacity()) { + sah = pool.nextAvailableSAH(); + pool.setAssociatedPath(sah, path, flags); + } else toss("SAH pool is full. Cannot create file", path); + if (!sah) toss("file not found:", path); + const file = { + path, + flags, + sah + }; + pool.mapS3FileToOFile(pFile, file); + file.lockType = capi.SQLITE_LOCK_NONE; + const sq3File = new capi.sqlite3_file(pFile); + sq3File.$pMethods = opfsIoMethods.pointer; + sq3File.dispose(); + wasm.poke32(pOutFlags, flags); + return 0; + } catch (e) { + pool.storeErr(e); + return capi.SQLITE_CANTOPEN; + } + } + }; + /** + Creates, initializes, and returns an sqlite3_vfs instance for an + OpfsSAHPool. The argument is the VFS's name (JS string). + + Throws if the VFS name is already registered or if something + goes terribly wrong via sqlite3.vfs.installVfs(). + + Maintenance reminder: the only detail about the returned object + which is specific to any given OpfsSAHPool instance is the $zName + member. All other state is identical. + */ + const createOpfsVfs = function(vfsName) { + if (sqlite3.capi.sqlite3_vfs_find(vfsName)) toss3("VFS name is already registered:", vfsName); + const opfsVfs = new capi.sqlite3_vfs(); + const pDVfs = capi.sqlite3_vfs_find(null); + const dVfs = pDVfs ? new capi.sqlite3_vfs(pDVfs) : null; + opfsVfs.$iVersion = 2; + opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE; + opfsVfs.addOnDispose(opfsVfs.$zName = wasm.allocCString(vfsName), () => setPoolForVfs(opfsVfs.pointer, 0)); + if (dVfs) { + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + dVfs.dispose(); + } + if (!opfsVfs.$xRandomness && !vfsMethods.xRandomness) vfsMethods.xRandomness = function(pVfs, nOut, pOut) { + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; + return i; + }; + if (!opfsVfs.$xSleep && !vfsMethods.xSleep) vfsMethods.xSleep = (pVfs, ms) => 0; + sqlite3.vfs.installVfs({ vfs: { + struct: opfsVfs, + methods: vfsMethods + } }); + return opfsVfs; + }; + /** + Class for managing OPFS-related state for the + OPFS SharedAccessHandle Pool sqlite3_vfs. + */ + class OpfsSAHPool { + vfsDir; + #dhVfsRoot; + #dhOpaque; + #dhVfsParent; + #mapSAHToName = /* @__PURE__ */ new Map(); + #mapFilenameToSAH = /* @__PURE__ */ new Map(); + #availableSAH = /* @__PURE__ */ new Set(); + #mapS3FileToOFile_ = /* @__PURE__ */ new Map(); + /** Buffer used by [sg]etAssociatedPath(). */ + #apBody = new Uint8Array(HEADER_CORPUS_SIZE); + #dvBody; + #cVfs; + #verbosity; + constructor(options = Object.create(null)) { + this.#verbosity = options.verbosity ?? optionDefaults.verbosity; + this.vfsName = options.name || optionDefaults.name; + this.#cVfs = createOpfsVfs(this.vfsName); + setPoolForVfs(this.#cVfs.pointer, this); + this.vfsDir = options.directory || "." + this.vfsName; + this.#dvBody = new DataView(this.#apBody.buffer, this.#apBody.byteOffset); + this.isReady = this.reset(!!(options.clearOnInit ?? optionDefaults.clearOnInit)).then(() => { + if (this.$error) throw this.$error; + return this.getCapacity() ? Promise.resolve(void 0) : this.addCapacity(options.initialCapacity || optionDefaults.initialCapacity); + }); + } + #logImpl(level, ...args) { + if (this.#verbosity > level) loggers[level](this.vfsName + ":", ...args); + } + log(...args) { + this.#logImpl(2, ...args); + } + warn(...args) { + this.#logImpl(1, ...args); + } + error(...args) { + this.#logImpl(0, ...args); + } + getVfs() { + return this.#cVfs; + } + getCapacity() { + return this.#mapSAHToName.size; + } + getFileCount() { + return this.#mapFilenameToSAH.size; + } + getFileNames() { + const rc = []; + for (const n of this.#mapFilenameToSAH.keys()) rc.push(n); + return rc; + } + /** + Adds n files to the pool's capacity. This change is + persistent across settings. Returns a Promise which resolves + to the new capacity. + */ + async addCapacity(n) { + for (let i = 0; i < n; ++i) { + const name = getRandomName(); + const ah = await (await this.#dhOpaque.getFileHandle(name, { create: true })).createSyncAccessHandle(); + this.#mapSAHToName.set(ah, name); + this.setAssociatedPath(ah, "", 0); + } + return this.getCapacity(); + } + /** + Reduce capacity by n, but can only reduce up to the limit + of currently-available SAHs. Returns a Promise which resolves + to the number of slots really removed. + */ + async reduceCapacity(n) { + let nRm = 0; + for (const ah of Array.from(this.#availableSAH)) { + if (nRm === n || this.getFileCount() === this.getCapacity()) break; + const name = this.#mapSAHToName.get(ah); + ah.close(); + await this.#dhOpaque.removeEntry(name); + this.#mapSAHToName.delete(ah); + this.#availableSAH.delete(ah); + ++nRm; + } + return nRm; + } + /** + Releases all currently-opened SAHs. The only legal operation + after this is acquireAccessHandles() or (if this is called from + pauseVfs()) either of isPaused() or unpauseVfs(). + */ + releaseAccessHandles() { + for (const ah of this.#mapSAHToName.keys()) ah.close(); + this.#mapSAHToName.clear(); + this.#mapFilenameToSAH.clear(); + this.#availableSAH.clear(); + } + /** + Opens all files under this.vfsDir/this.#dhOpaque and acquires a + SAH for each. Returns a Promise which resolves to no value but + completes once all SAHs are acquired. If acquiring an SAH + throws, this.$error will contain the corresponding Error + object. + + If it throws, it releases any SAHs which it may have + acquired before the exception was thrown, leaving the VFS in a + well-defined but unusable state. + + If clearFiles is true, the client-stored state of each file is + cleared when its handle is acquired, including its name, flags, + and any data stored after the metadata block. + */ + async acquireAccessHandles(clearFiles = false) { + const files = []; + for await (const [name, h] of this.#dhOpaque) if ("file" === h.kind) files.push([name, h]); + return Promise.all(files.map(async ([name, h]) => { + try { + const ah = await h.createSyncAccessHandle(); + this.#mapSAHToName.set(ah, name); + if (clearFiles) { + ah.truncate(HEADER_OFFSET_DATA); + this.setAssociatedPath(ah, "", 0); + } else { + const path = this.getAssociatedPath(ah); + if (path) this.#mapFilenameToSAH.set(path, ah); + else this.#availableSAH.add(ah); + } + } catch (e) { + this.storeErr(e); + this.releaseAccessHandles(); + throw e; + } + })); + } + /** + Given an SAH, returns the client-specified name of + that file by extracting it from the SAH's header. + + On error, it disassociates SAH from the pool and + returns an empty string. + */ + getAssociatedPath(sah) { + sah.read(this.#apBody, { at: 0 }); + const flags = this.#dvBody.getUint32(HEADER_OFFSET_FLAGS); + if (this.#apBody[0] && (flags & capi.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) { + warn(`Removing file with unexpected flags ${flags.toString(16)}`, this.#apBody); + this.setAssociatedPath(sah, "", 0); + return ""; + } + const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4); + sah.read(fileDigest, { at: HEADER_OFFSET_DIGEST }); + const compDigest = this.computeDigest(this.#apBody, flags); + if (fileDigest.every((v, i) => v === compDigest[i])) { + const pathBytes = this.#apBody.findIndex((v) => 0 === v); + if (0 === pathBytes) sah.truncate(HEADER_OFFSET_DATA); + return pathBytes ? textDecoder.decode(this.#apBody.subarray(0, pathBytes)) : ""; + } else { + warn("Disassociating file with bad digest."); + this.setAssociatedPath(sah, "", 0); + return ""; + } + } + /** + Stores the given client-defined path and SQLITE_OPEN_xyz flags + into the given SAH. If path is an empty string then the file is + disassociated from the pool but its previous name is preserved + in the metadata. + */ + setAssociatedPath(sah, path, flags) { + const enc = textEncoder.encodeInto(path, this.#apBody); + if (HEADER_MAX_PATH_SIZE <= enc.written + 1) toss("Path too long:", path); + if (path && flags) flags |= FLAG_COMPUTE_DIGEST_V2; + this.#apBody.fill(0, enc.written, HEADER_MAX_PATH_SIZE); + this.#dvBody.setUint32(HEADER_OFFSET_FLAGS, flags); + const digest = this.computeDigest(this.#apBody, flags); + sah.write(this.#apBody, { at: 0 }); + sah.write(digest, { at: HEADER_OFFSET_DIGEST }); + sah.flush(); + if (path) { + this.#mapFilenameToSAH.set(path, sah); + this.#availableSAH.delete(sah); + } else { + sah.truncate(HEADER_OFFSET_DATA); + this.#availableSAH.add(sah); + } + } + /** + Computes a digest for the given byte array and returns it as a + two-element Uint32Array. This digest gets stored in the + metadata for each file as a validation check. Changing this + algorithm invalidates all existing databases for this VFS, so + don't do that. + + See the docs for FLAG_COMPUTE_DIGEST_V2 for more details. + */ + computeDigest(byteArray, fileFlags) { + if (fileFlags & FLAG_COMPUTE_DIGEST_V2) { + let h1 = 3735928559; + let h2 = 1103547991; + for (const v of byteArray) { + h1 = Math.imul(h1 ^ v, 2654435761); + h2 = Math.imul(h2 ^ v, 104729); + } + return new Uint32Array([h1 >>> 0, h2 >>> 0]); + } else return new Uint32Array([0, 0]); + } + /** + Re-initializes the state of the SAH pool, releasing and + re-acquiring all handles. + + See acquireAccessHandles() for the specifics of the clearFiles + argument. + */ + async reset(clearFiles) { + await this.isReady; + let h = await navigator.storage.getDirectory(), prev; + for (const d of this.vfsDir.split("/")) if (d) { + prev = h; + h = await h.getDirectoryHandle(d, { create: true }); + } + this.#dhVfsRoot = h; + this.#dhVfsParent = prev; + this.#dhOpaque = await this.#dhVfsRoot.getDirectoryHandle(OPAQUE_DIR_NAME, { create: true }); + this.releaseAccessHandles(); + return this.acquireAccessHandles(clearFiles); + } + /** + Returns the pathname part of the given argument, + which may be any of: + + - a URL object + - A JS string representing a file name + - Wasm C-string representing a file name + + All "../" parts and duplicate slashes are resolve/removed from + the returned result. + */ + getPath(arg) { + if (wasm.isPtr(arg)) arg = wasm.cstrToJs(arg); + return (arg instanceof URL ? arg : new URL(arg, "file://localhost/")).pathname; + } + /** + Removes the association of the given client-specified file + name (JS string) from the pool. Returns true if a mapping + is found, else false. + */ + deletePath(path) { + const sah = this.#mapFilenameToSAH.get(path); + if (sah) { + this.#mapFilenameToSAH.delete(path); + this.setAssociatedPath(sah, "", 0); + } + return !!sah; + } + /** + Sets e (an Error object) as this object's current error. Pass a + falsy (or no) value to clear it. If code is truthy it is + assumed to be an SQLITE_xxx result code, defaulting to + SQLITE_IOERR if code is falsy. + + Returns the 2nd argument. + */ + storeErr(e, code) { + if (e) { + e.sqlite3Rc = code || capi.SQLITE_IOERR; + this.error(e); + } + this.$error = e; + return code; + } + /** + Pops this object's Error object and returns + it (a falsy value if no error is set). + */ + popErr() { + const rc = this.$error; + this.$error = void 0; + return rc; + } + /** + Returns the next available SAH without removing + it from the set. + */ + nextAvailableSAH() { + const [rc] = this.#availableSAH.keys(); + return rc; + } + /** + Given an (sqlite3_file*), returns the mapped + xOpen file object. + */ + getOFileForS3File(pFile) { + return this.#mapS3FileToOFile_.get(pFile); + } + /** + Maps or unmaps (if file is falsy) the given (sqlite3_file*) + to an xOpen file object and to this pool object. + */ + mapS3FileToOFile(pFile, file) { + if (file) { + this.#mapS3FileToOFile_.set(pFile, file); + setPoolForPFile(pFile, this); + } else { + this.#mapS3FileToOFile_.delete(pFile); + setPoolForPFile(pFile, false); + } + } + /** + Returns true if the given client-defined file name is in this + object's name-to-SAH map. + */ + hasFilename(name) { + return this.#mapFilenameToSAH.has(name); + } + /** + Returns the SAH associated with the given + client-defined file name. + */ + getSAHForPath(path) { + return this.#mapFilenameToSAH.get(path); + } + /** + Removes this object's sqlite3_vfs registration and shuts down + this object, releasing all handles, mappings, and whatnot, + including deleting its data directory. There is currently no + way to "revive" the object and reaquire its + resources. Similarly, there is no recovery strategy if removal + of any given SAH fails, so such errors are ignored by this + function. + + This function is intended primarily for testing. + + Resolves to true if it did its job, false if the + VFS has already been shut down. + + @see pauseVfs() + @see unpauseVfs() + */ + async removeVfs() { + if (!this.#cVfs.pointer || !this.#dhOpaque) return false; + capi.sqlite3_vfs_unregister(this.#cVfs.pointer); + this.#cVfs.dispose(); + delete initPromises[this.vfsName]; + try { + this.releaseAccessHandles(); + await this.#dhVfsRoot.removeEntry(OPAQUE_DIR_NAME, { recursive: true }); + this.#dhOpaque = void 0; + await this.#dhVfsParent.removeEntry(this.#dhVfsRoot.name, { recursive: true }); + this.#dhVfsRoot = this.#dhVfsParent = void 0; + } catch (e) { + sqlite3.config.error(this.vfsName, "removeVfs() failed with no recovery strategy:", e); + } + return true; + } + /** + "Pauses" this VFS by unregistering it from SQLite and + relinquishing all open SAHs, leaving the associated files + intact. If this object is already paused, this is a + no-op. Returns this object. + + This function throws if SQLite has any opened file handles + hosted by this VFS, as the alternative would be to invoke + Undefined Behavior by closing file handles out from under the + library. Similarly, automatically closing any database handles + opened by this VFS would invoke Undefined Behavior in + downstream code which is holding those pointers. + + If this function throws due to open file handles then it has + no side effects. If the OPFS API throws while closing handles + then the VFS is left in an undefined state. + + @see isPaused() + @see unpauseVfs() + */ + pauseVfs() { + if (this.#mapS3FileToOFile_.size > 0) sqlite3.SQLite3Error.toss(capi.SQLITE_MISUSE, "Cannot pause VFS", this.vfsName, "because it has opened files."); + if (this.#mapSAHToName.size > 0) { + capi.sqlite3_vfs_unregister(this.vfsName); + this.releaseAccessHandles(); + } + return this; + } + /** + Returns true if this pool is currently paused else false. + + @see pauseVfs() + @see unpauseVfs() + */ + isPaused() { + return 0 === this.#mapSAHToName.size; + } + /** + "Unpauses" this VFS, reacquiring all SAH's and (if successful) + re-registering it with SQLite. This is a no-op if the VFS is + not currently paused. + + The returned Promise resolves to this object. See + acquireAccessHandles() for how it behaves if it throws due to + SAH acquisition failure. + + @see isPaused() + @see pauseVfs() + */ + async unpauseVfs() { + if (0 === this.#mapSAHToName.size) return this.acquireAccessHandles(false).then(() => capi.sqlite3_vfs_register(this.#cVfs, 0), this); + return this; + } + //! Documented elsewhere in this file. + exportFile(name) { + const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:", name); + const n = sah.getSize() - HEADER_OFFSET_DATA; + const b = new Uint8Array(n > 0 ? n : 0); + if (n > 0) { + const nRead = sah.read(b, { at: HEADER_OFFSET_DATA }); + if (nRead != n) toss("Expected to read " + n + " bytes but read " + nRead + "."); + } + return b; + } + //! Impl for importDb() when its 2nd arg is a function. + async importDbChunked(name, callback) { + const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); + sah.truncate(0); + let nWrote = 0, chunk, checkedHeader = false; + try { + while (void 0 !== (chunk = await callback())) { + if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, { at: HEADER_OFFSET_DATA + nWrote }); + nWrote += chunk.byteLength; + } + if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); + if (!checkedHeader) { + const header = new Uint8Array(20); + sah.read(header, { at: 0 }); + util.affirmDbHeader(header); + } + sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); + } catch (e) { + this.setAssociatedPath(sah, "", 0); + throw e; + } + this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); + return nWrote; + } + //! Documented elsewhere in this file. + importDb(name, bytes) { + if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + else if (bytes instanceof Function) return this.importDbChunked(name, bytes); + const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); + const n = bytes.byteLength; + if (n < 512 || n % 512 != 0) toss("Byte array size is invalid for an SQLite db."); + const header = "SQLite format 3"; + for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss("Input does not contain an SQLite database header."); + const nWrote = sah.write(bytes, { at: HEADER_OFFSET_DATA }); + if (nWrote != n) { + this.setAssociatedPath(sah, "", 0); + toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); + } else { + sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); + this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); + } + return nWrote; + } + } + /** + A OpfsSAHPoolUtil instance is exposed to clients in order to + manipulate an OpfsSAHPool object without directly exposing that + object and allowing for some semantic changes compared to that + class. + + Class docs are in the client-level docs for + installOpfsSAHPoolVfs(). + */ + class OpfsSAHPoolUtil { + #p; + constructor(sahPool) { + this.#p = sahPool; + this.vfsName = sahPool.vfsName; + } + async addCapacity(n) { + return this.#p.addCapacity(n); + } + async reduceCapacity(n) { + return this.#p.reduceCapacity(n); + } + getCapacity() { + return this.#p.getCapacity(this.#p); + } + getFileCount() { + return this.#p.getFileCount(); + } + getFileNames() { + return this.#p.getFileNames(); + } + async reserveMinimumCapacity(min) { + const c = this.#p.getCapacity(); + return c < min ? this.#p.addCapacity(min - c) : c; + } + exportFile(name) { + return this.#p.exportFile(name); + } + importDb(name, bytes) { + return this.#p.importDb(name, bytes); + } + async wipeFiles() { + return this.#p.reset(true); + } + unlink(filename) { + return this.#p.deletePath(filename); + } + async removeVfs() { + return this.#p.removeVfs(); + } + pauseVfs() { + this.#p.pauseVfs(); + return this; + } + async unpauseVfs() { + return this.#p.unpauseVfs().then(() => this); + } + isPaused() { + return this.#p.isPaused(); + } + } + /** + Returns a resolved Promise if the current environment + has a "fully-sync" SAH impl, else a rejected Promise. + */ + const apiVersionCheck = async () => { + const dh = await navigator.storage.getDirectory(); + const fn = ".opfs-sahpool-sync-check-" + getRandomName(); + const close = (await (await dh.getFileHandle(fn, { create: true })).createSyncAccessHandle()).close(); + await close; + await dh.removeEntry(fn); + if (close?.then) toss("The local OPFS API is too old for opfs-sahpool:", "it has an async FileSystemSyncAccessHandle.close() method."); + return true; + }; + /** + installOpfsSAHPoolVfs() asynchronously initializes the OPFS + SyncAccessHandle (a.k.a. SAH) Pool VFS. It returns a Promise which + either resolves to a utility object described below or rejects with + an Error value. + + Initialization of this VFS is not automatic because its + registration requires that it lock all resources it + will potentially use, even if client code does not want + to use them. That, in turn, can lead to locking errors + when, for example, one page in a given origin has loaded + this VFS but does not use it, then another page in that + origin tries to use the VFS. If the VFS were automatically + registered, the second page would fail to load the VFS + due to OPFS locking errors. + + If this function is called more than once with a given "name" + option (see below), it will return the same Promise. Calls for + different names will return different Promises which resolve to + independent objects and refer to different VFS registrations. + + On success, the resulting Promise resolves to a utility object + which can be used to query and manipulate the pool. Its API is + described at the end of these docs. + + This function accepts an options object to configure certain + parts but it is only acknowledged for the very first call for + each distinct name and ignored for all subsequent calls with that + same name. + + The options, in alphabetical order: + + - `clearOnInit`: (default=false) if truthy, contents and filename + mapping are removed from each SAH it is acquired during + initialization of the VFS, leaving the VFS's storage in a pristine + state. Use this only for databases which need not survive a page + reload. + + - `initialCapacity`: (default=6) Specifies the default capacity of + the VFS. This should not be set unduly high because the VFS has + to open (and keep open) a file for each entry in the pool. This + setting only has an effect when the pool is initially empty. It + does not have any effect if a pool already exists. + + - `directory`: (default="."+`name`) Specifies the OPFS directory + name in which to store metadata for the `"opfs-sahpool"` + sqlite3_vfs. Only one instance of this VFS can be installed per + JavaScript engine, and any two engines with the same storage + directory name will collide with each other, leading to locking + errors and the inability to register the VFS in the second and + subsequent engine. Using a different directory name for each + application enables different engines in the same HTTP origin to + co-exist, but their data are invisible to each other. Changing + this name will effectively orphan any databases stored under + previous names. The default is unspecified but descriptive. This + option may contain multiple path elements, e.g. "foo/bar/baz", + and they are created automatically. In practice there should be + no driving need to change this. ACHTUNG: all files in this + directory are assumed to be managed by the VFS. Do not place + other files in that directory, as they may be deleted or + otherwise modified by the VFS. + + - `name`: (default="opfs-sahpool") sets the name to register this + VFS under. Normally this should not be changed, but it is + possible to register this VFS under multiple names so long as + each has its own separate directory to work from. The storage for + each is invisible to all others. The name must be a string + compatible with `sqlite3_vfs_register()` and friends and suitable + for use in URI-style database file names. + + Achtung: if a custom `name` is provided, a custom `directory` + must also be provided if any other instance is registered with + the default directory. If no directory is explicitly provided + then a directory name is synthesized from the `name` option. + + + - `forceReinitIfPreviouslyFailed`: (default=`false`) Is a fallback option + to assist in working around certain flaky environments which may + mysteriously fail to permit access to OPFS sync access handles on + an initial attempt but permit it on a second attemp. This option + should never be used but is provided for those who choose to + throw caution to the wind and trust such environments. If this + option is truthy _and_ the previous attempt to initialize this + VFS with the same `name` failed, the VFS will attempt to + initialize a second time instead of returning the cached + failure. See discussion at: + + + + Peculiarities of this VFS vis a vis other SQLite VFSes: + + - Paths given to it _must_ be absolute. Relative paths will not + be properly recognized. This is arguably a bug but correcting it + requires some hoop-jumping in routines which have no business + doing such tricks. (2026-01-19 (2.5 years later): the specifics + are lost to history, but this was a side effect of xOpen() + receiving an immutable C-string filename, to which no implicit + "/" can be prefixed without causing a discrepancy between what + the user provided and what the VFS stores. Its conceivable that + that quirk could be glossed over in xFullPathname(), but + regressions when doing so cannot be ruled out, so there are no + current plans to change this behavior.) + + - It is possible to install multiple instances under different + names, each sandboxed from one another inside their own private + directory. This feature exists primarily as a way for disparate + applications within a given HTTP origin to use this VFS without + introducing locking issues between them. + + + The API for the utility object passed on by this function's + Promise, in alphabetical order... + + - [async] number addCapacity(n) + + Adds `n` entries to the current pool. This change is persistent + across sessions so should not be called automatically at each app + startup (but see `reserveMinimumCapacity()`). Its returned Promise + resolves to the new capacity. Because this operation is necessarily + asynchronous, the C-level VFS API cannot call this on its own as + needed. + + - byteArray exportFile(name) + + Synchronously reads the contents of the given file into a Uint8Array + and returns it. This will throw if the given name is not currently + in active use or on I/O error. Note that the given name is _not_ + visible directly in OPFS (or, if it is, it's not from this VFS). + + - number getCapacity() + + Returns the number of files currently contained + in the SAH pool. The default capacity is only large enough for one + or two databases and their associated temp files. + + - number getFileCount() + + Returns the number of files from the pool currently allocated to + slots. This is not the same as the files being "opened". + + - array getFileNames() + + Returns an array of the names of the files currently allocated to + slots. This list is the same length as getFileCount(). + + - void importDb(name, bytes) + + Imports the contents of an SQLite database, provided as a byte + array or ArrayBuffer, under the given name, overwriting any + existing content. Throws if the pool has no available file slots, + on I/O error, or if the input does not appear to be a + database. In the latter case, only a cursory examination is made. + Results are undefined if the given db name refers to an opened + db. Note that this routine is _only_ for importing database + files, not arbitrary files, the reason being that this VFS will + automatically clean up any non-database files so importing them + is pointless. + + If passed a function for its second argument, its behavior + changes to asynchronous and it imports its data in chunks fed to + it by the given callback function. It calls the callback (which + may be async) repeatedly, expecting either a Uint8Array or + ArrayBuffer (to denote new input) or undefined (to denote + EOF). For so long as the callback continues to return + non-undefined, it will append incoming data to the given + VFS-hosted database file. The result of the resolved Promise when + called this way is the size of the resulting database. + + On success this routine rewrites the database header bytes in the + output file (not the input array) to force disabling of WAL mode. + + On a write error, the handle is removed from the pool and made + available for re-use. + + - [async] number reduceCapacity(n) + + Removes up to `n` entries from the pool, with the caveat that it can + only remove currently-unused entries. It returns a Promise which + resolves to the number of entries actually removed. + + - [async] boolean removeVfs() + + Unregisters the opfs-sahpool VFS and removes its directory from OPFS + (which means that _all client content_ is removed). After calling + this, the VFS may no longer be used and there is no way to re-add it + aside from reloading the current JavaScript context. + + Results are undefined if a database is currently in use with this + VFS. + + The returned Promise resolves to true if it performed the removal + and false if the VFS was not installed. + + If the VFS has a multi-level directory, e.g. "/foo/bar/baz", _only_ + the bottom-most directory is removed because this VFS cannot know for + certain whether the higher-level directories contain data which + should be removed. + + - [async] number reserveMinimumCapacity(min) + + If the current capacity is less than `min`, the capacity is + increased to `min`, else this returns with no side effects. The + resulting Promise resolves to the new capacity. + + - boolean unlink(filename) + + If a virtual file exists with the given name, disassociates it from + the pool and returns true, else returns false without side + effects. Results are undefined if the file is currently in active + use. + + - string vfsName + + The SQLite VFS name under which this pool's VFS is registered. + + - [async] void wipeFiles() + + Clears all client-defined state of all SAHs and makes all of them + available for re-use by the pool. Results are undefined if any such + handles are currently in use, e.g. by an sqlite3 db. + + APIs specific to the "pause" capability (added in version 3.49): + + Summary: "pausing" the VFS disassociates it from SQLite and + relinquishes its SAHs so that they may be opened by another + instance of this VFS (running in a separate tab/page or Worker). + "Unpausing" it takes back control, if able. + + - pauseVfs() + + "Pauses" this VFS by unregistering it from SQLite and + relinquishing all open SAHs, leaving the associated files intact. + This enables pages/tabs to coordinate semi-concurrent usage of + this VFS. If this object is already paused, this is a + no-op. Returns this object. Throws if SQLite has any opened file + handles hosted by this VFS. If this function throws due to open + file handles then it has no side effects. If the OPFS API throws + while closing handles then the VFS is left in an undefined state. + + - isPaused() + + Returns true if this VFS is paused, else false. + + - [async] unpauseVfs() + + Restores the VFS to an active state after having called + pauseVfs() on it. This is a no-op if the VFS is not paused. The + returned Promise resolves to this object on success. A rejected + Promise means there was a problem reacquiring the SAH handles + (possibly because they're in use by another instance or have + since been removed). Generically speaking, there is no recovery + strategy for that type of error, but if the problem is simply + that the OPFS files are locked, then a later attempt to unpause + it, made after the concurrent instance releases the SAHs, may + recover from the situation. + */ + sqlite3.installOpfsSAHPoolVfs = async function(options = Object.create(null)) { + options = Object.assign(Object.create(null), optionDefaults, options || {}); + const vfsName = options.name; + if (options.$testThrowPhase1) throw options.$testThrowPhase1; + if (initPromises[vfsName]) try { + return await initPromises[vfsName]; + } catch (e) { + if (options.forceReinitIfPreviouslyFailed) delete initPromises[vfsName]; + else throw e; + } + if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return initPromises[vfsName] = Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); + /** + Maintenance reminder: the order of ASYNC ops in this function + is significant. We need to have them all chained at the very + end in order to be able to catch a race condition where + installOpfsSAHPoolVfs() is called twice in rapid succession, + e.g.: + + installOpfsSAHPoolVfs().then(console.warn.bind(console)); + installOpfsSAHPoolVfs().then(console.warn.bind(console)); + + If the timing of the async calls is not "just right" then that + second call can end up triggering the init a second time and chaos + ensues. + */ + return initPromises[vfsName] = apiVersionCheck().then(async function() { + if (options.$testThrowPhase2) throw options.$testThrowPhase2; + const thePool = new OpfsSAHPool(options); + return thePool.isReady.then(async () => { + /** The poolUtil object will be the result of the + resolved Promise. */ + const poolUtil = new OpfsSAHPoolUtil(thePool); + if (sqlite3.oo1) { + const oo1 = sqlite3.oo1; + const theVfs = thePool.getVfs(); + const OpfsSAHPoolDb = function(...args) { + const opt = oo1.DB.dbCtorHelper.normalizeArgs(...args); + opt.vfs = theVfs.$zName; + oo1.DB.dbCtorHelper.call(this, opt); + }; + OpfsSAHPoolDb.prototype = Object.create(oo1.DB.prototype); + poolUtil.OpfsSAHPoolDb = OpfsSAHPoolDb; + } + thePool.log("VFS initialized."); + return poolUtil; + }).catch(async (e) => { + await thePool.removeVfs().catch(() => {}); + throw e; + }); + }).catch((err) => { + return initPromises[vfsName] = Promise.reject(err); + }); + }; + }); + try { + const bootstrapConfig = Object.assign( + Object.create(null), + /** The WASM-environment-dependent configuration for sqlite3ApiBootstrap() */ + { + memory: "undefined" !== typeof wasmMemory ? wasmMemory : EmscriptenModule["wasmMemory"], + exports: "undefined" !== typeof wasmExports ? wasmExports : Object.prototype.hasOwnProperty.call(EmscriptenModule, "wasmExports") ? EmscriptenModule["wasmExports"] : EmscriptenModule["asm"] + }, + globalThis.sqlite3ApiBootstrap.defaultConfig, + globalThis.sqlite3ApiConfig || {} + ); + sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig); + /** + For purposes of the Emscripten build, call sqlite3ApiBootstrap(). + Ideally clients should be able to inject their own config here, + but that's not practical in this particular build constellation + because of the order everything happens in. Clients may either + define globalThis.sqlite3ApiConfig or modify + globalThis.sqlite3ApiBootstrap.defaultConfig to tweak the default + configuration used by a no-args call to sqlite3ApiBootstrap(), + but must have first loaded their WASM module in order to be able + to provide the necessary configuration state. + */ + const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig); + delete globalThis.sqlite3ApiBootstrap; + return p; + } catch (e) { + console.error("sqlite3ApiBootstrap() error:", e); + throw e; + } + }; + if (runtimeInitialized) moduleRtn = Module; + else moduleRtn = new Promise((resolve, reject) => { + readyPromiseResolve = resolve; + readyPromiseReject = reject; + }); + return moduleRtn; +} +sqlite3InitModule = (function() { + /** + In order to hide the sqlite3InitModule()'s resulting + Emscripten module from downstream clients (and simplify our + documentation by being able to elide those details), we hide that + function and expose a hand-written sqlite3InitModule() to return + the sqlite3 object (most of the time). + */ + const originalInit = sqlite3InitModule; + if (!originalInit) throw new Error("Expecting sqlite3InitModule to be defined by the Emscripten build."); + /** + We need to add some state which our custom Module.locateFile() + can see, but an Emscripten limitation currently prevents us from + attaching it to the sqlite3InitModule function object: + + https://github.com/emscripten-core/emscripten/issues/18071 + + The only(?) current workaround is to temporarily stash this state + into the global scope and delete it when sqlite3InitModule() + is called. + */ + const sIMS = globalThis.sqlite3InitModuleState = Object.assign(Object.create(null), { + moduleScript: globalThis?.document?.currentScript, + isWorker: "undefined" !== typeof WorkerGlobalScope, + location: globalThis.location, + urlParams: globalThis?.location?.href ? new URL(globalThis.location.href).searchParams : new URLSearchParams(), + wasmFilename: "sqlite3.wasm" + }); + sIMS.debugModule = sIMS.urlParams.has("sqlite3.debugModule") ? (...args) => console.warn("sqlite3.debugModule:", ...args) : () => {}; + if (sIMS.urlParams.has("sqlite3.dir")) sIMS.sqlite3Dir = sIMS.urlParams.get("sqlite3.dir") + "/"; + else if (sIMS.moduleScript) { + const li = sIMS.moduleScript.src.split("/"); + li.pop(); + sIMS.sqlite3Dir = li.join("/") + "/"; + } + const sIM = globalThis.sqlite3InitModule = function ff(...args) { + sIMS.emscriptenLocateFile = args[0]?.locateFile; + sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm; + return originalInit(...args).then((EmscriptenModule) => { + sIMS.debugModule("sqlite3InitModule() sIMS =", sIMS); + sIMS.debugModule("sqlite3InitModule() EmscriptenModule =", EmscriptenModule); + const s = EmscriptenModule.runSQLite3PostLoadInit(sIMS, EmscriptenModule, !!ff.__isUnderTest); + sIMS.debugModule("sqlite3InitModule() sqlite3 =", s); + return s; + }).catch((e) => { + console.error("Exception loading sqlite3 module:", e); + throw e; + }); + }; + sIM.ready = originalInit.ready; + if (sIMS.moduleScript) { + let src = sIMS.moduleScript.src.split("/"); + src.pop(); + sIMS.scriptDir = src.join("/") + "/"; + } + sIMS.debugModule("extern-post-js.c-pp.js sqlite3InitModuleState =", sIMS); + return sIM; +})(); +//#endregion +//#region src/bin/sqlite3-worker1.mjs +sqlite3InitModule().then((sqlite3) => sqlite3.initWorker1API()); +//#endregion +export {}; diff --git a/src/web/sqlite-wasm/sqlite3.wasm b/src/web/sqlite-wasm/sqlite3.wasm new file mode 100644 index 0000000000000000000000000000000000000000..28b092090992a3db2b84c5e66094629503135b07 GIT binary patch literal 859730 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

xPUEKbH3YPcP2d1bVJtb7^o&&rpT{mD~L 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

xPUEKbH3YPcP2d1bVJtb7^o&&rpT{mD~L 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

O%sgbWed-C0+wF{6s-$l2;%VRk@yZ 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 diff --git a/yarn.lock b/yarn.lock index 0195d11b..02827e7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2196,6 +2196,7 @@ __metadata: version: 0.0.0-use.local resolution: "@op-engineering/op-sqlite@workspace:." dependencies: + "@sqlite.org/sqlite-wasm": "npm:^3.51.2-build8" "@types/better-sqlite3": "npm:^7.6.13" "@types/jest": "npm:^30.0.0" better-sqlite3: "npm:^12.5.0" @@ -2947,6 +2948,13 @@ __metadata: languageName: node linkType: hard +"@sqlite.org/sqlite-wasm@npm:^3.51.2-build8": + version: 3.51.2-build8 + resolution: "@sqlite.org/sqlite-wasm@npm:3.51.2-build8" + checksum: 10c0/f193298c13a2e156897ca4dcae524f4c84c6004b7175baefc36dacec35b1ab3f7868f69d0ce975adfe64cb20c2cd15efba6285d3a48439ae4b556eb7f923e749 + languageName: node + linkType: hard + "@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" From dbcbbb51eeadfc8aa52071f6c8387fd8c3b9491a Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Mon, 6 Apr 2026 07:49:22 -0400 Subject: [PATCH 3/4] Remove temp build files --- example/.gitignore | 1 + example/web-build/assets/index-DmS4xevg.js | 17 - example/web-build/assets/index-bI9uVmc1.js | 3 - .../web-build/assets/sqlite3-DGXXSD5r.wasm | Bin 859730 -> 0 bytes .../sqlite3-opfs-async-proxy-BWKAW6aw.js | 1 - .../assets/sqlite3-worker1-CEle-zSR.mjs | 15461 ---------------- .../assets/sqlite3-worker1-jtPPE4oU.js | 3 - example/web-build/index.html | 28 - 8 files changed, 1 insertion(+), 15513 deletions(-) delete mode 100644 example/web-build/assets/index-DmS4xevg.js delete mode 100644 example/web-build/assets/index-bI9uVmc1.js delete mode 100644 example/web-build/assets/sqlite3-DGXXSD5r.wasm delete mode 100644 example/web-build/assets/sqlite3-opfs-async-proxy-BWKAW6aw.js delete mode 100644 example/web-build/assets/sqlite3-worker1-CEle-zSR.mjs delete mode 100644 example/web-build/assets/sqlite3-worker1-jtPPE4oU.js delete mode 100644 example/web-build/index.html diff --git a/example/.gitignore b/example/.gitignore index f704db64..87b5eea0 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -52,6 +52,7 @@ yarn-error.log # Bundle artifact *.jsbundle +web-build/ # Ruby / CocoaPods /ios/Pods/ diff --git a/example/web-build/assets/index-DmS4xevg.js b/example/web-build/assets/index-DmS4xevg.js deleted file mode 100644 index e9188f71..00000000 --- a/example/web-build/assets/index-DmS4xevg.js +++ /dev/null @@ -1,17 +0,0 @@ -(function(){const u=document.createElement("link").relList;if(u&&u.supports&&u.supports("modulepreload"))return;for(const f of document.querySelectorAll('link[rel="modulepreload"]'))c(f);new MutationObserver(f=>{for(const d of f)if(d.type==="childList")for(const h of d.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&c(h)}).observe(document,{childList:!0,subtree:!0});function o(f){const d={};return f.integrity&&(d.integrity=f.integrity),f.referrerPolicy&&(d.referrerPolicy=f.referrerPolicy),f.crossOrigin==="use-credentials"?d.credentials="include":f.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function c(f){if(f.ep)return;f.ep=!0;const d=o(f);fetch(f.href,d)}})();var Cg=r=>r.disabled||Array.isArray(r.accessibilityStates)&&r.accessibilityStates.indexOf("disabled")>-1,Mg={adjustable:"slider",button:"button",header:"heading",image:"img",imagebutton:null,keyboardkey:null,label:null,link:"link",none:"presentation",search:"search",summary:"region",text:null},zm=r=>{var u=r.accessibilityRole,o=r.role,c=o||u;if(c){var f=Mg[c];if(f!==null)return f||c}},Dg={article:"article",banner:"header",blockquote:"blockquote",button:"button",code:"code",complementary:"aside",contentinfo:"footer",deletion:"del",emphasis:"em",figure:"figure",insertion:"ins",form:"form",list:"ul",listitem:"li",main:"main",navigation:"nav",paragraph:"p",region:"section",strong:"strong"},wg={},zg=function(u){u===void 0&&(u=wg);var o=u.role||u.accessibilityRole;if(o==="label")return"label";var c=zm(u);if(c){if(c==="heading"){var f=u.accessibilityLevel||u["aria-level"];return f!=null?"h"+f:"h1"}return Dg[c]}},Nm={isDisabled:Cg,propsToAccessibilityComponent:zg,propsToAriaRole:zm};function Ci(r){"@babel/helpers - typeof";return Ci=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(u){return typeof u}:function(u){return u&&typeof Symbol=="function"&&u.constructor===Symbol&&u!==Symbol.prototype?"symbol":typeof u},Ci(r)}function Ng(r,u){if(Ci(r)!="object"||!r)return r;var o=r[Symbol.toPrimitive];if(o!==void 0){var c=o.call(r,u);if(Ci(c)!="object")return c;throw new TypeError("@@toPrimitive must return a primitive value.")}return(u==="string"?String:Number)(r)}function Bg(r){var u=Ng(r,"string");return Ci(u)=="symbol"?u:u+""}function Hg(r,u,o){return(u=Bg(u))in r?Object.defineProperty(r,u,{value:o,enumerable:!0,configurable:!0,writable:!0}):r[u]=o,r}function uy(r,u){var o=Object.keys(r);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(r);u&&(c=c.filter(function(f){return Object.getOwnPropertyDescriptor(r,f).enumerable})),o.push.apply(o,c)}return o}function Ot(r){for(var u=1;ur+u.charAt(0).toUpperCase()+u.substring(1);Object.keys(cc).forEach(r=>{Ug.forEach(u=>{cc[Lg(u,r)]=cc[r]})});var qg=r=>r==="currentcolor"||r==="currentColor"||r==="inherit"||r.indexOf("var(")===0;function ia(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}function Bm(r){if(Object.prototype.hasOwnProperty.call(r,"__esModule"))return r;var u=r.default;if(typeof u=="function"){var o=function c(){var f=!1;try{f=this instanceof c}catch{}return f?Reflect.construct(u,arguments,this.constructor):u.apply(this,arguments)};o.prototype=u.prototype}else o={};return Object.defineProperty(o,"__esModule",{value:!0}),Object.keys(r).forEach(function(c){var f=Object.getOwnPropertyDescriptor(r,c);Object.defineProperty(o,c,f.get?f:{enumerable:!0,get:function(){return r[c]}})}),o}var bf,cy;function jg(){if(cy)return bf;cy=1;function r(A){if(typeof A=="number")return A>>>0===A&&A>=0&&A<=4294967295?A:null;if(typeof A!="string")return null;const z=R();let w;if(w=z.hex6.exec(A))return parseInt(w[1]+"ff",16)>>>0;const Y=Q(A);return Y??((w=z.rgb.exec(A))?(x(w[1])<<24|x(w[2])<<16|x(w[3])<<8|255)>>>0:(w=z.rgba.exec(A))?w[6]!==void 0?(x(w[6])<<24|x(w[7])<<16|x(w[8])<<8|q(w[9]))>>>0:(x(w[2])<<24|x(w[3])<<16|x(w[4])<<8|q(w[5]))>>>0:(w=z.hex3.exec(A))?parseInt(w[1]+w[1]+w[2]+w[2]+w[3]+w[3]+"ff",16)>>>0:(w=z.hex8.exec(A))?parseInt(w[1],16)>>>0:(w=z.hex4.exec(A))?parseInt(w[1]+w[1]+w[2]+w[2]+w[3]+w[3]+w[4]+w[4],16)>>>0:(w=z.hsl.exec(A))?(o(M(w[1]),L(w[2]),L(w[3]))|255)>>>0:(w=z.hsla.exec(A))?w[6]!==void 0?(o(M(w[6]),L(w[7]),L(w[8]))|q(w[9]))>>>0:(o(M(w[2]),L(w[3]),L(w[4]))|q(w[5]))>>>0:(w=z.hwb.exec(A))?(c(M(w[1]),L(w[2]),L(w[3]))|255)>>>0:null)}function u(A,z,w){return w<0&&(w+=1),w>1&&(w-=1),w<1/6?A+(z-A)*6*w:w<1/2?z:w<2/3?A+(z-A)*(2/3-w)*6:A}function o(A,z,w){const Y=w<.5?w*(1+z):w+z-w*z,J=2*w-Y,j=u(J,Y,A+1/3),G=u(J,Y,A),ne=u(J,Y,A-1/3);return Math.round(j*255)<<24|Math.round(G*255)<<16|Math.round(ne*255)<<8}function c(A,z,w){if(z+w>=1){const G=Math.round(z*255/(z+w));return G<<24|G<<16|G<<8}const Y=u(0,1,A+1/3)*(1-z-w)+z,J=u(0,1,A)*(1-z-w)+z,j=u(0,1,A-1/3)*(1-z-w)+z;return Math.round(Y*255)<<24|Math.round(J*255)<<16|Math.round(j*255)<<8}const f="[-+]?\\d*\\.?\\d+",d=f+"%";function h(...A){return"\\(\\s*("+A.join(")\\s*,?\\s*(")+")\\s*\\)"}function b(...A){return"\\(\\s*("+A.slice(0,A.length-1).join(")\\s*,?\\s*(")+")\\s*/\\s*("+A[A.length-1]+")\\s*\\)"}function S(...A){return"\\(\\s*("+A.join(")\\s*,\\s*(")+")\\s*\\)"}let y;function R(){return y===void 0&&(y={rgb:new RegExp("rgb"+h(f,f,f)),rgba:new RegExp("rgba("+S(f,f,f,f)+"|"+b(f,f,f,f)+")"),hsl:new RegExp("hsl"+h(f,d,d)),hsla:new RegExp("hsla("+S(f,d,d,f)+"|"+b(f,d,d,f)+")"),hwb:new RegExp("hwb"+h(f,d,d)),hex3:/^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex4:/^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#([0-9a-fA-F]{6})$/,hex8:/^#([0-9a-fA-F]{8})$/}),y}function x(A){const z=parseInt(A,10);return z<0?0:z>255?255:z}function M(A){return(parseFloat(A)%360+360)%360/360}function q(A){const z=parseFloat(A);return z<0?0:z>1?255:Math.round(z*255)}function L(A){const z=parseFloat(A);return z<0?0:z>100?1:z/100}function Q(A){switch(A){case"transparent":return 0;case"aliceblue":return 4042850303;case"antiquewhite":return 4209760255;case"aqua":return 16777215;case"aquamarine":return 2147472639;case"azure":return 4043309055;case"beige":return 4126530815;case"bisque":return 4293182719;case"black":return 255;case"blanchedalmond":return 4293643775;case"blue":return 65535;case"blueviolet":return 2318131967;case"brown":return 2771004159;case"burlywood":return 3736635391;case"burntsienna":return 3934150143;case"cadetblue":return 1604231423;case"chartreuse":return 2147418367;case"chocolate":return 3530104575;case"coral":return 4286533887;case"cornflowerblue":return 1687547391;case"cornsilk":return 4294499583;case"crimson":return 3692313855;case"cyan":return 16777215;case"darkblue":return 35839;case"darkcyan":return 9145343;case"darkgoldenrod":return 3095792639;case"darkgray":return 2846468607;case"darkgreen":return 6553855;case"darkgrey":return 2846468607;case"darkkhaki":return 3182914559;case"darkmagenta":return 2332068863;case"darkolivegreen":return 1433087999;case"darkorange":return 4287365375;case"darkorchid":return 2570243327;case"darkred":return 2332033279;case"darksalmon":return 3918953215;case"darkseagreen":return 2411499519;case"darkslateblue":return 1211993087;case"darkslategray":return 793726975;case"darkslategrey":return 793726975;case"darkturquoise":return 13554175;case"darkviolet":return 2483082239;case"deeppink":return 4279538687;case"deepskyblue":return 12582911;case"dimgray":return 1768516095;case"dimgrey":return 1768516095;case"dodgerblue":return 512819199;case"firebrick":return 2988581631;case"floralwhite":return 4294635775;case"forestgreen":return 579543807;case"fuchsia":return 4278255615;case"gainsboro":return 3705462015;case"ghostwhite":return 4177068031;case"gold":return 4292280575;case"goldenrod":return 3668254975;case"gray":return 2155905279;case"green":return 8388863;case"greenyellow":return 2919182335;case"grey":return 2155905279;case"honeydew":return 4043305215;case"hotpink":return 4285117695;case"indianred":return 3445382399;case"indigo":return 1258324735;case"ivory":return 4294963455;case"khaki":return 4041641215;case"lavender":return 3873897215;case"lavenderblush":return 4293981695;case"lawngreen":return 2096890111;case"lemonchiffon":return 4294626815;case"lightblue":return 2916673279;case"lightcoral":return 4034953471;case"lightcyan":return 3774873599;case"lightgoldenrodyellow":return 4210742015;case"lightgray":return 3553874943;case"lightgreen":return 2431553791;case"lightgrey":return 3553874943;case"lightpink":return 4290167295;case"lightsalmon":return 4288707327;case"lightseagreen":return 548580095;case"lightskyblue":return 2278488831;case"lightslategray":return 2005441023;case"lightslategrey":return 2005441023;case"lightsteelblue":return 2965692159;case"lightyellow":return 4294959359;case"lime":return 16711935;case"limegreen":return 852308735;case"linen":return 4210091775;case"magenta":return 4278255615;case"maroon":return 2147483903;case"mediumaquamarine":return 1724754687;case"mediumblue":return 52735;case"mediumorchid":return 3126187007;case"mediumpurple":return 2473647103;case"mediumseagreen":return 1018393087;case"mediumslateblue":return 2070474495;case"mediumspringgreen":return 16423679;case"mediumturquoise":return 1221709055;case"mediumvioletred":return 3340076543;case"midnightblue":return 421097727;case"mintcream":return 4127193855;case"mistyrose":return 4293190143;case"moccasin":return 4293178879;case"navajowhite":return 4292783615;case"navy":return 33023;case"oldlace":return 4260751103;case"olive":return 2155872511;case"olivedrab":return 1804477439;case"orange":return 4289003775;case"orangered":return 4282712319;case"orchid":return 3664828159;case"palegoldenrod":return 4008225535;case"palegreen":return 2566625535;case"paleturquoise":return 2951671551;case"palevioletred":return 3681588223;case"papayawhip":return 4293907967;case"peachpuff":return 4292524543;case"peru":return 3448061951;case"pink":return 4290825215;case"plum":return 3718307327;case"powderblue":return 2967529215;case"purple":return 2147516671;case"rebeccapurple":return 1714657791;case"red":return 4278190335;case"rosybrown":return 3163525119;case"royalblue":return 1097458175;case"saddlebrown":return 2336560127;case"salmon":return 4202722047;case"sandybrown":return 4104413439;case"seagreen":return 780883967;case"seashell":return 4294307583;case"sienna":return 2689740287;case"silver":return 3233857791;case"skyblue":return 2278484991;case"slateblue":return 1784335871;case"slategray":return 1887473919;case"slategrey":return 1887473919;case"snow":return 4294638335;case"springgreen":return 16744447;case"steelblue":return 1182971135;case"tan":return 3535047935;case"teal":return 8421631;case"thistle":return 3636451583;case"tomato":return 4284696575;case"turquoise":return 1088475391;case"violet":return 4001558271;case"wheat":return 4125012991;case"white":return 4294967295;case"whitesmoke":return 4126537215;case"yellow":return 4294902015;case"yellowgreen":return 2597139199}return null}return bf=r,bf}var Yg=jg();const Gg=ia(Yg);var Xg=r=>{if(r==null)return r;var u=Gg(r);if(u!=null)return u=(u<<24|u>>>8)>>>0,u},gs=function(u,o){if(o===void 0&&(o=1),u!=null){if(typeof u=="string"&&qg(u))return u;var c=Xg(u);if(c!=null){var f=c>>16&255,d=c>>8&255,h=c&255,b=(c>>24&255)/255,S=(b*o).toFixed(2);return"rgba("+f+","+d+","+h+","+S+")"}}},Vg={backgroundColor:!0,borderColor:!0,borderTopColor:!0,borderRightColor:!0,borderBottomColor:!0,borderLeftColor:!0,color:!0,shadowColor:!0,textDecorationColor:!0,textShadowColor:!0};function St(r,u){var o=r;return(u==null||!cc[u])&&typeof r=="number"?o=r+"px":u!=null&&Vg[u]&&(o=gs(r)),o}var qt=!!(typeof window<"u"&&window.document&&window.document.createElement),kg={},Qg=!qt||window.CSS!=null&&window.CSS.supports!=null&&(window.CSS.supports("text-decoration-line","none")||window.CSS.supports("-webkit-text-decoration-line","none")),Kg="monospace,monospace",oy='-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif',Zg={borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderBlockColor:["borderTopColor","borderBottomColor"],borderInlineColor:["borderRightColor","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderStyle:["borderTopStyle","borderRightStyle","borderBottomStyle","borderLeftStyle"],borderBlockStyle:["borderTopStyle","borderBottomStyle"],borderInlineStyle:["borderRightStyle","borderLeftStyle"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],borderBlockWidth:["borderTopWidth","borderBottomWidth"],borderInlineWidth:["borderRightWidth","borderLeftWidth"],insetBlock:["top","bottom"],insetInline:["left","right"],marginBlock:["marginTop","marginBottom"],marginInline:["marginRight","marginLeft"],paddingBlock:["paddingTop","paddingBottom"],paddingInline:["paddingRight","paddingLeft"],overflow:["overflowX","overflowY"],overscrollBehavior:["overscrollBehaviorX","overscrollBehaviorY"],borderBlockStartColor:["borderTopColor"],borderBlockStartStyle:["borderTopStyle"],borderBlockStartWidth:["borderTopWidth"],borderBlockEndColor:["borderBottomColor"],borderBlockEndStyle:["borderBottomStyle"],borderBlockEndWidth:["borderBottomWidth"],borderEndStartRadius:["borderBottomLeftRadius"],borderEndEndRadius:["borderBottomRightRadius"],borderStartStartRadius:["borderTopLeftRadius"],borderStartEndRadius:["borderTopRightRadius"],insetBlockEnd:["bottom"],insetBlockStart:["top"],marginBlockStart:["marginTop"],marginBlockEnd:["marginBottom"],paddingBlockStart:["paddingTop"],paddingBlockEnd:["paddingBottom"]},Hm=(r,u)=>{if(!r)return kg;var o={},c=function(){var b=r[f];if(b==null)return"continue";if(f==="backgroundClip")b==="text"&&(o.backgroundClip=b,o.WebkitBackgroundClip=b);else if(f==="flex")b===-1?(o.flexGrow=0,o.flexShrink=1,o.flexBasis="auto"):o.flex=b;else if(f==="font")o[f]=b.replace("System",oy);else if(f==="fontFamily")if(b.indexOf("System")>-1){var S=b.split(/,\s*/);S[S.indexOf("System")]=oy,o[f]=S.join(",")}else b==="monospace"?o[f]=Kg:o[f]=b;else if(f==="textDecorationLine")Qg?o.textDecorationLine=b:o.textDecoration=b;else if(f==="writingDirection")o.direction=b;else{var y=St(r[f],f),R=Zg[f];u&&f==="inset"?(r.insetInline==null&&(o.left=y,o.right=y),r.insetBlock==null&&(o.top=y,o.bottom=y)):u&&f==="margin"?(r.marginInline==null&&(o.marginLeft=y,o.marginRight=y),r.marginBlock==null&&(o.marginTop=y,o.marginBottom=y)):u&&f==="padding"?(r.paddingInline==null&&(o.paddingLeft=y,o.paddingRight=y),r.paddingBlock==null&&(o.paddingTop=y,o.paddingBottom=y)):R?R.forEach((x,M)=>{r[x]==null&&(o[x]=y)}):o[f]=y}};for(var f in r)var d=c();return o};function Wg(r,u){for(var o=r.length,c=u^o,f=0,d;o>=4;)d=r.charCodeAt(f)&255|(r.charCodeAt(++f)&255)<<8|(r.charCodeAt(++f)&255)<<16|(r.charCodeAt(++f)&255)<<24,d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16),d^=d>>>24,d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16),c=(c&65535)*1540483477+(((c>>>16)*1540483477&65535)<<16)^d,o-=4,++f;switch(o){case 3:c^=(r.charCodeAt(f+2)&255)<<16;case 2:c^=(r.charCodeAt(f+1)&255)<<8;case 1:c^=r.charCodeAt(f)&255,c=(c&65535)*1540483477+(((c>>>16)*1540483477&65535)<<16)}return c^=c>>>13,c=(c&65535)*1540483477+(((c>>>16)*1540483477&65535)<<16),c^=c>>>15,c>>>0}var Pg=r=>Wg(r,1).toString(36),Jg=/[A-Z]/g,$g=/^ms-/,Sf={};function Ig(r){return"-"+r.toLowerCase()}function Fg(r){if(r in Sf)return Sf[r];var u=r.replace(Jg,Ig);return Sf[r]=$g.test(u)?"-"+u:u}var Pu={},Ju={},$u={},fy;function Um(){if(fy)return $u;fy=1,Object.defineProperty($u,"__esModule",{value:!0}),$u.default=r;function r(u){return u.charAt(0).toUpperCase()+u.slice(1)}return $u}var sy;function e0(){if(sy)return Ju;sy=1,Object.defineProperty(Ju,"__esModule",{value:!0}),Ju.default=c;var r=Um(),u=o(r);function o(f){return f&&f.__esModule?f:{default:f}}function c(f,d,h){var b=f[d];if(b&&h.hasOwnProperty(d))for(var S=(0,u.default)(d),y=0;y0&&(L[Q]=z)}else{var j=(0,c.default)(M,Q,A,L,x);j&&(L[Q]=j),L=(0,u.default)(x,Q,L)}}return L}}return Pu}var r0=n0();const i0=ia(r0);var tc={};function oc(r){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?oc=function(o){return typeof o}:oc=function(o){return o&&typeof Symbol=="function"&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o},oc(r)}function u0(r){return s0(r)||f0(r)||o0(r)||c0()}function c0(){throw new TypeError(`Invalid attempt to spread non-iterable instance. -In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function o0(r,u){if(r){if(typeof r=="string")return Wf(r,u);var o=Object.prototype.toString.call(r).slice(8,-1);if(o==="Object"&&r.constructor&&(o=r.constructor.name),o==="Map"||o==="Set")return Array.from(o);if(o==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o))return Wf(r,u)}}function f0(r){if(typeof Symbol<"u"&&Symbol.iterator in Object(r))return Array.from(r)}function s0(r){if(Array.isArray(r))return Wf(r)}function Wf(r,u){(u==null||u>r.length)&&(u=r.length);for(var o=0,c=new Array(u);o-1)return c.map(function(b){return h.replace(/image-set\(/g,b+"image-set(")})}return ac}var Y0=j0();const G0=ia(Y0);var lc={},Ty;function X0(){if(Ty)return lc;Ty=1,Object.defineProperty(lc,"__esModule",{value:!0}),lc.default=u;var r={marginBlockStart:["WebkitMarginBefore"],marginBlockEnd:["WebkitMarginAfter"],marginInlineStart:["WebkitMarginStart","MozMarginStart"],marginInlineEnd:["WebkitMarginEnd","MozMarginEnd"],paddingBlockStart:["WebkitPaddingBefore"],paddingBlockEnd:["WebkitPaddingAfter"],paddingInlineStart:["WebkitPaddingStart","MozPaddingStart"],paddingInlineEnd:["WebkitPaddingEnd","MozPaddingEnd"],borderBlockStart:["WebkitBorderBefore"],borderBlockStartColor:["WebkitBorderBeforeColor"],borderBlockStartStyle:["WebkitBorderBeforeStyle"],borderBlockStartWidth:["WebkitBorderBeforeWidth"],borderBlockEnd:["WebkitBorderAfter"],borderBlockEndColor:["WebkitBorderAfterColor"],borderBlockEndStyle:["WebkitBorderAfterStyle"],borderBlockEndWidth:["WebkitBorderAfterWidth"],borderInlineStart:["WebkitBorderStart","MozBorderStart"],borderInlineStartColor:["WebkitBorderStartColor","MozBorderStartColor"],borderInlineStartStyle:["WebkitBorderStartStyle","MozBorderStartStyle"],borderInlineStartWidth:["WebkitBorderStartWidth","MozBorderStartWidth"],borderInlineEnd:["WebkitBorderEnd","MozBorderEnd"],borderInlineEndColor:["WebkitBorderEndColor","MozBorderEndColor"],borderInlineEndStyle:["WebkitBorderEndStyle","MozBorderEndStyle"],borderInlineEndWidth:["WebkitBorderEndWidth","MozBorderEndWidth"]};function u(o,c,f){if(Object.prototype.hasOwnProperty.call(r,o))for(var d=r[o],h=0,b=d.length;h-1&&Y!=="order")for(var J=M[w],j=0,G=J.length;j-1)return A;var z=Q.split(/,(?![^()]*(?:\([^()]*\))?\))/g).filter(function(w){return!/-webkit-|-ms-/.test(w)}).join(",");return x.indexOf("Moz")>-1?z:(q["Webkit"+(0,d.default)(x)]=A,q["Moz"+(0,d.default)(x)]=z,Q)}}return ic}var ep=F0();const tp=ia(ep);var Ne=["Webkit"],ap=["Moz"],lp=["Webkit","Moz"],Xe=["Webkit","ms"],np=["Webkit","Moz","ms"];const rp={plugins:[q0,G0,k0,Z0,J0,tp],prefixMap:{appearance:np,userSelect:lp,textEmphasisPosition:Xe,textEmphasis:Xe,textEmphasisStyle:Xe,textEmphasisColor:Xe,boxDecorationBreak:Xe,clipPath:Ne,maskImage:Xe,maskMode:Xe,maskRepeat:Xe,maskPosition:Xe,maskClip:Xe,maskOrigin:Xe,maskSize:Xe,maskComposite:Xe,mask:Xe,maskBorderSource:Xe,maskBorderMode:Xe,maskBorderSlice:Xe,maskBorderWidth:Xe,maskBorderOutset:Xe,maskBorderRepeat:Xe,maskBorder:Xe,maskType:Xe,textDecorationStyle:Ne,textDecorationSkip:Ne,textDecorationLine:Ne,textDecorationColor:Ne,filter:Ne,breakAfter:Ne,breakBefore:Ne,breakInside:Ne,columnCount:Ne,columnFill:Ne,columnGap:Ne,columnRule:Ne,columnRuleColor:Ne,columnRuleStyle:Ne,columnRuleWidth:Ne,columns:Ne,columnSpan:Ne,columnWidth:Ne,backdropFilter:Ne,hyphens:Ne,flowInto:Ne,flowFrom:Ne,regionFragment:Ne,textOrientation:Ne,tabSize:ap,fontKerning:Ne,textSizeAdjust:Ne}};var ip=i0(rp),up=["animationKeyframes"],Cy=new Map,cp={},op=1,fp=3,sp={borderColor:2,borderRadius:2,borderStyle:2,borderWidth:2,display:2,flex:2,inset:2,margin:2,overflow:2,overscrollBehavior:2,padding:2,insetBlock:2.1,insetInline:2.1,marginInline:2.1,marginBlock:2.1,paddingInline:2.1,paddingBlock:2.1,borderBlockStartColor:2.2,borderBlockStartStyle:2.2,borderBlockStartWidth:2.2,borderBlockEndColor:2.2,borderBlockEndStyle:2.2,borderBlockEndWidth:2.2,borderInlineStartColor:2.2,borderInlineStartStyle:2.2,borderInlineStartWidth:2.2,borderInlineEndColor:2.2,borderInlineEndStyle:2.2,borderInlineEndWidth:2.2,borderEndStartRadius:2.2,borderEndEndRadius:2.2,borderStartStartRadius:2.2,borderStartEndRadius:2.2,insetBlockEnd:2.2,insetBlockStart:2.2,insetInlineEnd:2.2,insetInlineStart:2.2,marginBlockStart:2.2,marginBlockEnd:2.2,marginInlineStart:2.2,marginInlineEnd:2.2,paddingBlockStart:2.2,paddingBlockEnd:2.2,paddingInlineStart:2.2,paddingInlineEnd:2.2},Pf="borderTopLeftRadius",Jf="borderTopRightRadius",$f="borderBottomLeftRadius",If="borderBottomRightRadius",Ff="borderLeftColor",es="borderLeftStyle",ts="borderLeftWidth",as="borderRightColor",ls="borderRightStyle",ns="borderRightWidth",rs="right",is="marginLeft",us="marginRight",cs="paddingLeft",os="paddingRight",fs="left",dc={[Pf]:Jf,[Jf]:Pf,[$f]:If,[If]:$f,[Ff]:as,[es]:ls,[ts]:ns,[as]:Ff,[ls]:es,[ns]:ts,[fs]:rs,[is]:us,[us]:is,[cs]:os,[os]:cs,[rs]:fs},Oi={borderStartStartRadius:Pf,borderStartEndRadius:Jf,borderEndStartRadius:$f,borderEndEndRadius:If,borderInlineStartColor:Ff,borderInlineStartStyle:es,borderInlineStartWidth:ts,borderInlineEndColor:as,borderInlineEndStyle:ls,borderInlineEndWidth:ns,insetInlineEnd:rs,insetInlineStart:fs,marginInlineStart:is,marginInlineEnd:us,paddingInlineStart:cs,paddingInlineEnd:os},Vm=["clear","float","textAlign"];function dp(r){var u={$$css:!0},o=[];function c(f,d,h){var b=yp(h,d),S=d+b,y=Cy.get(S),R;if(y!=null)R=y[0],o.push(y[1]);else{var x=f!==d?S:b;R=ps("r",f,x);var M=sp[f]||fp,q=mp(R,d,h),L=[q,M];o.push(L),Cy.set(S,[R,L])}return R}return Object.keys(r).sort().forEach(f=>{var d=r[f];if(d!=null){var h;if(Vm.indexOf(f)>-1){var b=c(f,f,"left"),S=c(f,f,"right");d==="start"?h=[b,S]:d==="end"&&(h=[S,b])}var y=Oi[f];if(y!=null){var R=c(f,y,d),x=c(f,dc[y],d);h=[R,x]}if(f==="transitionProperty"){for(var M=Array.isArray(d)?d:[d],q=[],L=0;L0){var A=[...M],z=[...M];q.forEach(w=>{var Y=A[w];if(typeof Y=="string"){var J=Oi[Y],j=dc[J];A[w]=J,z[w]=j;var G=c(f,f,A),ne=c(f,f,z);h=[G,ne]}})}}h==null?h=c(f,f,d):u.$$css$localize=!0,u[f]=h}}),[u,o]}function vp(r,u){var o={$$css:!0},c=[],f=r.animationKeyframes,d=Bl(r,up),h=ps("css",u,JSON.stringify(r)),b="."+h,S;if(f!=null){var y=km(f),R=y[0],x=y[1];S=R.join(","),c.push(...x)}var M=ba(Ot(Ot({},d),{},{animationName:S}));return c.push(""+b+M),o[h]=h,[o,[[c,op]]]}function hp(r,u){var o=r||cp,c={},f={},d=function(){var y=o[h],R=h,x=y;if(!Object.prototype.hasOwnProperty.call(o,h)||y==null)return"continue";Vm.indexOf(h)>-1&&(y==="start"?x=u?"right":"left":y==="end"&&(x=u?"left":"right"));var M=Oi[h];if(M!=null&&(R=u?dc[M]:M),h==="transitionProperty"){var q=Array.isArray(y)?y:[y];q.forEach((L,Q)=>{if(typeof L=="string"){var A=Oi[L];A!=null&&(q[Q]=u?dc[A]:A,x=q.join(" "))}})}c[R]||(f[R]=x),R===h&&(c[R]=!0)};for(var h in o)var b=d();return Hm(f,!0)}function yp(r,u){var o=St(r,u);return typeof o!="string"?JSON.stringify(o||""):o}function mp(r,u,o){var c=[],f="."+r;switch(u){case"animationKeyframes":{var d=km(o),h=d[0],b=d[1],S=ba({animationName:h.join(",")});c.push(""+f+S,...b);break}case"placeholderTextColor":{var y=ba({color:o,opacity:1});c.push(f+"::-webkit-input-placeholder"+y,f+"::-moz-placeholder"+y,f+":-ms-input-placeholder"+y,f+"::placeholder"+y);break}case"pointerEvents":{var R=o;if(o==="auto")R="auto!important";else if(o==="none"){R="none!important";var x=ba({pointerEvents:"none"});c.push(f+">* "+x)}else if(o==="box-none"){R="none!important";var M=ba({pointerEvents:"auto"});c.push(f+">* "+M)}else if(o==="box-only"){R="auto!important";var q=ba({pointerEvents:"none"});c.push(f+">* "+q)}var L=ba({pointerEvents:R});c.push(""+f+L);break}case"scrollbarWidth":{o==="none"&&c.push(f+"::-webkit-scrollbar{display:none}");var Q=ba({scrollbarWidth:o});c.push(""+f+Q);break}default:{var A=ba({[u]:o});c.push(""+f+A);break}}return c}function ba(r){var u=ip(Hm(r)),o=Object.keys(u).map(c=>{var f=u[c],d=Fg(c);return Array.isArray(f)?f.map(h=>d+":"+h).join(";"):d+":"+f}).sort().join(";");return"{"+o+";}"}function ps(r,u,o){var c=Pg(u+o);return r+"-"+c}function bp(r){var u=["-webkit-",""],o=ps("r","animation",JSON.stringify(r)),c="{"+Object.keys(r).map(d=>{var h=r[d],b=ba(h);return""+d+b}).join("")+"}",f=u.map(d=>"@"+d+"keyframes "+o+c);return[o,f]}function km(r){if(typeof r=="number")throw new Error("Invalid CSS keyframes type: "+typeof r);var u=[],o=[],c=Array.isArray(r)?r:[r];return c.forEach(f=>{if(typeof f=="string")u.push(f);else{var d=bp(f),h=d[0],b=d[1];u.push(h),o.push(...b)}}),[u,o]}function Of(r,u,o){if(qt){var c=u??document,f=c.getElementById(r);if(f==null)if(f=document.createElement("style"),f.setAttribute("id",r),typeof o=="string"&&f.appendChild(document.createTextNode(o)),c instanceof ShadowRoot)c.insertBefore(f,c.firstChild);else{var d=c.head;d&&d.insertBefore(f,d.firstChild)}return f.sheet}else return null}var Sp=Array.prototype.slice;function Af(r){var u={},o={};if(r!=null){var c;Sp.call(r.cssRules).forEach((h,b)=>{var S=h.cssText;if(S.indexOf("stylesheet-group")>-1)c=Rp(h),u[c]={start:b,rules:[S]};else{var y=Dy(S);y!=null&&(o[y]=!0,u[c].rules.push(S))}})}function f(h,b,S){var y=My(u),R=y.indexOf(b),x=R+1,M=y[x],q=M!=null&&u[M].start!=null?u[M].start:h.cssRules.length,L=Tp(h,S,q);if(L){u[b].start==null&&(u[b].start=q);for(var Q=x;Q{var b=u[h].rules,S=b.shift();return b.sort(),b.unshift(S),b.join(` -`)}).join(` -`)},insert(h,b){var S=Number(b);if(u[S]==null){var y=gp(S);u[S]={start:null,rules:[y]},r!=null&&f(r,S,y)}var R=Dy(h);if(R!=null&&o[R]==null&&(o[R]=!0,u[S].rules.push(h),r!=null)){var x=f(r,S,h);x||u[S].rules.pop()}}};return d}function gp(r){return'[stylesheet-group="'+r+'"]{}'}var pp=/["']/g;function Rp(r){return Number(r.selectorText.split(pp)[1])}function My(r){return Object.keys(r).map(Number).sort((u,o)=>u>o?1:-1)}var Ep=/\s*([,])\s*/g;function Dy(r){var u=r.split("{")[0].trim();return u!==""?u.replace(Ep,"$1"):null}function Tp(r,u,o){try{return r.insertRule(u,o),!0}catch{return!1}}var xp="react-native-stylesheet",_f=new WeakMap,aa=[],wy=["html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}","body{margin:0;}","button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}","input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}"];function Rs(r,u){u===void 0&&(u=xp);var o;if(qt){var c=r!=null?r.getRootNode():document;if(aa.length===0)o=Af(Of(u)),wy.forEach(b=>{o.insert(b,0)}),_f.set(c,aa.length),aa.push(o);else{var f=_f.get(c);if(f==null){var d=aa[0],h=d!=null?d.getTextContent():"";o=Af(Of(u,c,h)),_f.set(c,aa.length),aa.push(o)}else o=aa[f]}}else aa.length===0?(o=Af(Of(u)),wy.forEach(b=>{o.insert(b,0)}),aa.push(o)):o=aa[0];return{getTextContent(){return o.getTextContent()},id:u,insert(b,S){aa.forEach(y=>{y.insert(b,S)})}}}var uc={},zy;function Op(){if(zy)return uc;zy=1,Object.defineProperty(uc,"__esModule",{value:!0}),uc.localizeStyle=c;var r=new WeakMap,u="$$css$localize";function o(f,d){var h={};for(var b in f)if(b!==u){var S=f[b];Array.isArray(S)?h[b]=d?S[1]:S[0]:h[b]=S}return h}function c(f,d){if(f[u]!=null){var h=d?1:0;if(r.has(f)){var b=r.get(f),S=b[h];return S==null&&(S=o(f,d),b[h]=S,r.set(f,b)),S}var y=o(f,d),R=new Array(2);return R[h]=y,r.set(f,R),y}return f}return uc}var Cf,Ny;function Ap(){return Ny||(Ny=1,Cf=Op()),Cf}var _p=Ap(),Cp={},Qm={height:0,width:0},Mp=r=>{var u=r.shadowColor,o=r.shadowOffset,c=r.shadowOpacity,f=r.shadowRadius,d=o||Qm,h=d.height,b=d.width,S=St(b),y=St(h),R=St(f||0),x=gs(u||"black",c);if(x!=null&&S!=null&&y!=null&&R!=null)return S+" "+y+" "+R+" "+x},Dp=r=>{var u=r.textShadowColor,o=r.textShadowOffset,c=r.textShadowRadius,f=o||Qm,d=f.height,h=f.width,b=c||0,S=St(h),y=St(d),R=St(b),x=St(u,"textShadowColor");if(x&&(d!==0||h!==0||b!==0)&&S!=null&&y!=null&&R!=null)return S+" "+y+" "+R+" "+x},wp=r=>{if(typeof r=="string")return r;var u=St(r.offsetX)||0,o=St(r.offsetY)||0,c=St(r.blurRadius)||0,f=St(r.spreadDistance)||0,d=gs(r.color)||"black",h=r.inset?"inset ":"";return""+h+u+" "+o+" "+c+" "+f+" "+d},zp=r=>r.map(wp).join(", "),Np=r=>{var u=Object.keys(r)[0],o=r[u];if(u==="matrix"||u==="matrix3d")return u+"("+o.join(",")+")";var c=St(o,u);return u+"("+c+")"},Bp=r=>r.map(Np).join(" "),Hp=r=>r.map(u=>St(u)).join(" "),Up={borderBottomEndRadius:"borderEndEndRadius",borderBottomStartRadius:"borderEndStartRadius",borderTopEndRadius:"borderStartEndRadius",borderTopStartRadius:"borderStartStartRadius",borderEndColor:"borderInlineEndColor",borderEndStyle:"borderInlineEndStyle",borderEndWidth:"borderInlineEndWidth",borderStartColor:"borderInlineStartColor",borderStartStyle:"borderInlineStartStyle",borderStartWidth:"borderInlineStartWidth",end:"insetInlineEnd",marginEnd:"marginInlineEnd",marginHorizontal:"marginInline",marginStart:"marginInlineStart",marginVertical:"marginBlock",paddingEnd:"paddingInlineEnd",paddingHorizontal:"paddingInline",paddingStart:"paddingInlineStart",paddingVertical:"paddingBlock",start:"insetInlineStart"},Lp={elevation:!0,overlayColor:!0,resizeMode:!0,tintColor:!0},Km=function(u,o){o===void 0&&(o={});var c=u||Cp,f={};if(o.shadow,c.shadowColor!=null||c.shadowOffset!=null||c.shadowOpacity!=null||c.shadowRadius!=null){var d=Mp(c);d!=null&&(f.boxShadow=d)}if(o.textShadow,c.textShadowColor!=null||c.textShadowOffset!=null||c.textShadowRadius!=null){var h=Dp(c);if(h!=null&&f.textShadow==null){var b=c.textShadow,S=b?b+", "+h:h;f.textShadow=S}}for(var y in c)if(!(Lp[y]!=null||y==="shadowColor"||y==="shadowOffset"||y==="shadowOpacity"||y==="shadowRadius"||y==="textShadowColor"||y==="textShadowOffset"||y==="textShadowRadius")){var R=c[y],x=Up[y]||y,M=R;if(!(!Object.prototype.hasOwnProperty.call(c,y)||x!==y&&c[x]!=null))if(x==="aspectRatio"&&typeof M=="number")f[x]=M.toString();else if(x==="boxShadow"){Array.isArray(M)&&(M=zp(M));var q=f.boxShadow;f.boxShadow=q?M+", "+q:M}else x==="fontVariant"?(Array.isArray(M)&&M.length>0&&(M=M.join(" ")),f[x]=M):x==="textAlignVertical"?c.verticalAlign==null&&(f.verticalAlign=M==="center"?"middle":M):x==="transform"?(Array.isArray(M)&&(M=Bp(M)),f.transform=M):x==="transformOrigin"?(Array.isArray(M)&&(M=Hp(M)),f.transformOrigin=M):f[x]=M}return f},hi={},By;function qp(){if(By)return hi;By=1,Object.defineProperty(hi,"__esModule",{value:!0}),hi.styleq=void 0;var r=new WeakMap,u="$$css";function o(f){var d,h,b;return f!=null&&(d=f.disableCache===!0,h=f.disableMix===!0,b=f.transform),function(){for(var y=[],R="",x=null,M=d?null:r,q=new Array(arguments.length),L=0;L0;){var Q=q.pop();if(!(Q==null||Q===!1)){if(Array.isArray(Q)){for(var A=0;A{var o=u[0],c=u[1];vc!=null&&o.forEach(f=>{vc.insert(f,c)})})}function Xp(r){var u=dp(Km(r,Wm)),o=u[0],c=u[1];return Pm(c),o}function Vp(r,u){var o=vp(r,u),c=o[0],f=o[1];return Pm(f),c}var Jm={position:"absolute",left:0,right:0,top:0,bottom:0},kp=$m({x:Ot({},Jm)}).x;function $m(r){return Object.keys(r).forEach(u=>{var o=r[u];if(o!=null&&o.$$css!==!0){var c;u.indexOf("$raw")>-1?c=Vp(o,u.split("$raw")[0]):c=Xp(o),Zm.set(o,c)}}),r}function Qp(r,u){return[r,u]}function Kp(){for(var r=arguments.length,u=new Array(r),o=0;o{u||(u=Pp);var c=u,f=c["aria-activedescendant"],d=c.accessibilityActiveDescendant,h=c["aria-atomic"],b=c.accessibilityAtomic,S=c["aria-autocomplete"],y=c.accessibilityAutoComplete,R=c["aria-busy"],x=c.accessibilityBusy,M=c["aria-checked"],q=c.accessibilityChecked,L=c["aria-colcount"],Q=c.accessibilityColumnCount,A=c["aria-colindex"],z=c.accessibilityColumnIndex,w=c["aria-colspan"],Y=c.accessibilityColumnSpan,J=c["aria-controls"],j=c.accessibilityControls,G=c["aria-current"],ne=c.accessibilityCurrent,ie=c["aria-describedby"],me=c.accessibilityDescribedBy,be=c["aria-details"],we=c.accessibilityDetails,pe=c["aria-disabled"],Te=c.accessibilityDisabled,Qe=c["aria-errormessage"],Ae=c.accessibilityErrorMessage,D=c["aria-expanded"],X=c.accessibilityExpanded,K=c["aria-flowto"],de=c.accessibilityFlowTo,g=c["aria-haspopup"],H=c.accessibilityHasPopup,k=c["aria-hidden"],V=c.accessibilityHidden,$=c["aria-invalid"],ue=c.accessibilityInvalid,te=c["aria-keyshortcuts"],je=c.accessibilityKeyShortcuts,ce=c["aria-label"],At=c.accessibilityLabel,Za=c["aria-labelledby"],Wa=c.accessibilityLabelledBy,pa=c["aria-level"],Pa=c.accessibilityLevel,Jt=c["aria-live"],Hl=c.accessibilityLiveRegion,hn=c["aria-modal"],gt=c.accessibilityModal,Ul=c["aria-multiline"],et=c.accessibilityMultiline,ca=c["aria-multiselectable"],_t=c.accessibilityMultiSelectable,jt=c["aria-orientation"],Ja=c.accessibilityOrientation,Ll=c["aria-owns"],ql=c.accessibilityOwns,oa=c["aria-placeholder"],tt=c.accessibilityPlaceholder,Ct=c["aria-posinset"],at=c.accessibilityPosInSet,yn=c["aria-pressed"],ir=c.accessibilityPressed,mn=c["aria-readonly"],$a=c.accessibilityReadOnly,Ra=c["aria-required"],Re=c.accessibilityRequired;c.role,c.accessibilityRole;var Ea=c["aria-roledescription"],Ta=c.accessibilityRoleDescription,bn=c["aria-rowcount"],Sn=c.accessibilityRowCount,jl=c["aria-rowindex"],Yl=c.accessibilityRowIndex,ee=c["aria-rowspan"],Le=c.accessibilityRowSpan,dt=c["aria-selected"],Ia=c.accessibilitySelected,Yt=c["aria-setsize"],Fa=c.accessibilitySetSize,ur=c["aria-sort"],Sc=c.accessibilitySort,$t=c["aria-valuemax"],Je=c.accessibilityValueMax,lt=c["aria-valuemin"],el=c.accessibilityValueMin,gn=c["aria-valuenow"],gc=c.accessibilityValueNow,Hi=c["aria-valuetext"],Ui=c.accessibilityValueText,fa=c.dataSet,tl=c.focusable,xa=c.id,al=c.nativeID,ll=c.pointerEvents,nl=c.style,He=c.tabIndex,cr=c.testID,W=Bl(c,Wp),Oa=pe||Te,ut=Nm.propsToAriaRole(u),Li=f??d;Li!=null&&(W["aria-activedescendant"]=Li);var or=h!=null?f:b;or!=null&&(W["aria-atomic"]=or);var fr=S??y;fr!=null&&(W["aria-autocomplete"]=fr);var qi=R??x;qi!=null&&(W["aria-busy"]=qi);var Gl=M??q;Gl!=null&&(W["aria-checked"]=Gl);var Xl=L??Q;Xl!=null&&(W["aria-colcount"]=Xl);var Gt=A??z;Gt!=null&&(W["aria-colindex"]=Gt);var pn=w??Y;pn!=null&&(W["aria-colspan"]=pn);var sr=J??j;sr!=null&&(W["aria-controls"]=nr(sr));var Aa=G??ne;Aa!=null&&(W["aria-current"]=Aa);var Rn=ie??me;Rn!=null&&(W["aria-describedby"]=nr(Rn));var En=be??we;En!=null&&(W["aria-details"]=En),Oa===!0&&(W["aria-disabled"]=!0,(r==="button"||r==="form"||r==="input"||r==="select"||r==="textarea")&&(W.disabled=!0));var ji=Qe??Ae;ji!=null&&(W["aria-errormessage"]=ji);var dr=D??X;dr!=null&&(W["aria-expanded"]=dr);var vt=K??de;vt!=null&&(W["aria-flowto"]=nr(vt));var vr=g??H;vr!=null&&(W["aria-haspopup"]=vr);var Yi=k??V;Yi===!0&&(W["aria-hidden"]=Yi);var Vl=$??ue;Vl!=null&&(W["aria-invalid"]=Vl);var hr=te??je;hr!=null&&(W["aria-keyshortcuts"]=nr(hr));var kl=ce??At;kl!=null&&(W["aria-label"]=kl);var Gi=Za??Wa;Gi!=null&&(W["aria-labelledby"]=nr(Gi));var ht=pa??Pa;ht!=null&&(W["aria-level"]=ht);var Ql=Jt??Hl;Ql!=null&&(W["aria-live"]=Ql==="none"?"off":Ql);var yr=hn??gt;yr!=null&&(W["aria-modal"]=yr);var Tn=Ul??et;Tn!=null&&(W["aria-multiline"]=Tn);var _a=ca??_t;_a!=null&&(W["aria-multiselectable"]=_a);var mr=jt??Ja;mr!=null&&(W["aria-orientation"]=mr);var br=Ll??ql;br!=null&&(W["aria-owns"]=nr(br));var Ca=oa??tt;Ca!=null&&(W["aria-placeholder"]=Ca);var Xi=Ct??at;Xi!=null&&(W["aria-posinset"]=Xi);var Sr=yn??ir;Sr!=null&&(W["aria-pressed"]=Sr);var gr=mn??$a;gr!=null&&(W["aria-readonly"]=gr,(r==="input"||r==="select"||r==="textarea")&&(W.readOnly=!0));var xn=Ra??Re;xn!=null&&(W["aria-required"]=xn,(r==="input"||r==="select"||r==="textarea")&&(W.required=Re)),ut!=null&&(W.role=ut==="none"?"presentation":ut);var Vi=Ea??Ta;Vi!=null&&(W["aria-roledescription"]=Vi);var ki=bn??Sn;ki!=null&&(W["aria-rowcount"]=ki);var Kl=jl??Yl;Kl!=null&&(W["aria-rowindex"]=Kl);var On=ee??Le;On!=null&&(W["aria-rowspan"]=On);var An=dt??Ia;An!=null&&(W["aria-selected"]=An);var Ma=Yt??Fa;Ma!=null&&(W["aria-setsize"]=Ma);var Da=ur??Sc;Da!=null&&(W["aria-sort"]=Da);var pr=$t??Je;pr!=null&&(W["aria-valuemax"]=pr);var _n=lt??el;_n!=null&&(W["aria-valuemin"]=_n);var Rr=gn??gc;Rr!=null&&(W["aria-valuenow"]=Rr);var rl=Hi??Ui;if(rl!=null&&(W["aria-valuetext"]=rl),fa!=null){for(var Mt in fa)if(Jp.call(fa,Mt)){var Er=e1(Mt),il=fa[Mt];il!=null&&(W["data-"+Er]=il)}}He===0||He==="0"||He===-1||He==="-1"?W.tabIndex=He:(tl===!1&&(W.tabIndex="-1"),r==="a"||r==="button"||r==="input"||r==="select"||r==="textarea"?(tl===!1||Te===!0)&&(W.tabIndex="-1"):ut==="button"||ut==="checkbox"||ut==="link"||ut==="radio"||ut==="textbox"||ut==="switch"?tl!==!1&&(W.tabIndex="0"):tl===!0&&(W.tabIndex="0"));var It=ua([nl,ll&&t1[ll]],Ot({writingDirection:"ltr"},o)),Cn=It[0],Zl=It[1];Cn&&(W.className=Cn),Zl&&(W.style=Zl);var Tr=xa??al;return Tr!=null&&(W.id=Tr),cr!=null&&(W["data-testid"]=cr),W.type==null&&r==="button"&&(W.type="button"),W},Mf={exports:{}},oe={};var Hy;function l1(){if(Hy)return oe;Hy=1;var r=Symbol.for("react.transitional.element"),u=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),f=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),h=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),S=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),R=Symbol.for("react.lazy"),x=Symbol.iterator;function M(g){return g===null||typeof g!="object"?null:(g=x&&g[x]||g["@@iterator"],typeof g=="function"?g:null)}var q={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},L=Object.assign,Q={};function A(g,H,k){this.props=g,this.context=H,this.refs=Q,this.updater=k||q}A.prototype.isReactComponent={},A.prototype.setState=function(g,H){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,H,"setState")},A.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function z(){}z.prototype=A.prototype;function w(g,H,k){this.props=g,this.context=H,this.refs=Q,this.updater=k||q}var Y=w.prototype=new z;Y.constructor=w,L(Y,A.prototype),Y.isPureReactComponent=!0;var J=Array.isArray,j={H:null,A:null,T:null,S:null,V:null},G=Object.prototype.hasOwnProperty;function ne(g,H,k,V,$,ue){return k=ue.ref,{$$typeof:r,type:g,key:H,ref:k!==void 0?k:null,props:ue}}function ie(g,H){return ne(g.type,H,void 0,void 0,void 0,g.props)}function me(g){return typeof g=="object"&&g!==null&&g.$$typeof===r}function be(g){var H={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(k){return H[k]})}var we=/\/+/g;function pe(g,H){return typeof g=="object"&&g!==null&&g.key!=null?be(""+g.key):H.toString(36)}function Te(){}function Qe(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(Te,Te):(g.status="pending",g.then(function(H){g.status==="pending"&&(g.status="fulfilled",g.value=H)},function(H){g.status==="pending"&&(g.status="rejected",g.reason=H)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function Ae(g,H,k,V,$){var ue=typeof g;(ue==="undefined"||ue==="boolean")&&(g=null);var te=!1;if(g===null)te=!0;else switch(ue){case"bigint":case"string":case"number":te=!0;break;case"object":switch(g.$$typeof){case r:case u:te=!0;break;case R:return te=g._init,Ae(te(g._payload),H,k,V,$)}}if(te)return $=$(g),te=V===""?"."+pe(g,0):V,J($)?(k="",te!=null&&(k=te.replace(we,"$&/")+"/"),Ae($,H,k,"",function(At){return At})):$!=null&&(me($)&&($=ie($,k+($.key==null||g&&g.key===$.key?"":(""+$.key).replace(we,"$&/")+"/")+te)),H.push($)),1;te=0;var je=V===""?".":V+":";if(J(g))for(var ce=0;ce{var c;r&&r.constructor===String&&(c=Nm.propsToAccessibilityComponent(u));var f=c||r,d=a1(f,u,o),h=st.createElement(f,d),b=d.dir?st.createElement(u1,{children:h,direction:d.dir,locale:d.lang}):h;return b},Df={exports:{}},yi={},wf={exports:{}},zf={};var jy;function c1(){return jy||(jy=1,(function(r){function u(D,X){var K=D.length;D.push(X);e:for(;0>>1,g=D[de];if(0>>1;def(V,K))$f(ue,V)?(D[de]=ue,D[$]=K,de=$):(D[de]=V,D[k]=K,de=k);else if($f(ue,K))D[de]=ue,D[$]=K,de=$;else break e}}return X}function f(D,X){var K=D.sortIndex-X.sortIndex;return K!==0?K:D.id-X.id}if(r.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;r.unstable_now=function(){return d.now()}}else{var h=Date,b=h.now();r.unstable_now=function(){return h.now()-b}}var S=[],y=[],R=1,x=null,M=3,q=!1,L=!1,Q=!1,A=!1,z=typeof setTimeout=="function"?setTimeout:null,w=typeof clearTimeout=="function"?clearTimeout:null,Y=typeof setImmediate<"u"?setImmediate:null;function J(D){for(var X=o(y);X!==null;){if(X.callback===null)c(y);else if(X.startTime<=D)c(y),X.sortIndex=X.expirationTime,u(S,X);else break;X=o(y)}}function j(D){if(Q=!1,J(D),!L)if(o(S)!==null)L=!0,G||(G=!0,pe());else{var X=o(y);X!==null&&Ae(j,X.startTime-D)}}var G=!1,ne=-1,ie=5,me=-1;function be(){return A?!0:!(r.unstable_now()-meD&&be());){var de=x.callback;if(typeof de=="function"){x.callback=null,M=x.priorityLevel;var g=de(x.expirationTime<=D);if(D=r.unstable_now(),typeof g=="function"){x.callback=g,J(D),X=!0;break t}x===o(S)&&c(S),J(D)}else c(S);x=o(S)}if(x!==null)X=!0;else{var H=o(y);H!==null&&Ae(j,H.startTime-D),X=!1}}break e}finally{x=null,M=K,q=!1}X=void 0}}finally{X?pe():G=!1}}}var pe;if(typeof Y=="function")pe=function(){Y(we)};else if(typeof MessageChannel<"u"){var Te=new MessageChannel,Qe=Te.port2;Te.port1.onmessage=we,pe=function(){Qe.postMessage(null)}}else pe=function(){z(we,0)};function Ae(D,X){ne=z(function(){D(r.unstable_now())},X)}r.unstable_IdlePriority=5,r.unstable_ImmediatePriority=1,r.unstable_LowPriority=4,r.unstable_NormalPriority=3,r.unstable_Profiling=null,r.unstable_UserBlockingPriority=2,r.unstable_cancelCallback=function(D){D.callback=null},r.unstable_forceFrameRate=function(D){0>D||125de?(D.sortIndex=K,u(y,D),o(S)===null&&D===o(y)&&(Q?(w(ne),ne=-1):Q=!0,Ae(j,K-de))):(D.sortIndex=g,u(S,D),L||q||(L=!0,G||(G=!0,pe()))),D},r.unstable_shouldYield=be,r.unstable_wrapCallback=function(D){var X=M;return function(){var K=M;M=X;try{return D.apply(this,arguments)}finally{M=K}}}})(zf)),zf}var Yy;function o1(){return Yy||(Yy=1,wf.exports=c1()),wf.exports}var Nf={exports:{}},ot={};var Gy;function f1(){if(Gy)return ot;Gy=1;var r=Es();function u(S){var y="https://react.dev/errors/"+S;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(u){console.error(u)}}return r(),Nf.exports=f1(),Nf.exports}var Vy;function d1(){if(Vy)return yi;Vy=1;var r=o1(),u=Es(),o=s1();function c(e){var t="https://react.dev/errors/"+e;if(1g||(e.current=de[g],de[g]=null,g--)}function V(e,t){g++,de[g]=e.current,e.current=t}var $=H(null),ue=H(null),te=H(null),je=H(null);function ce(e,t){switch(V(te,t),V(ue,e),V($,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Hh(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Hh(t),e=Uh(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}k($),V($,e)}function At(){k($),k(ue),k(te)}function Za(e){e.memoizedState!==null&&V(je,e);var t=$.current,a=Uh(t,e.type);t!==a&&(V(ue,e),V($,a))}function Wa(e){ue.current===e&&(k($),k(ue)),je.current===e&&(k(je),oi._currentValue=K)}var pa=Object.prototype.hasOwnProperty,Pa=r.unstable_scheduleCallback,Jt=r.unstable_cancelCallback,Hl=r.unstable_shouldYield,hn=r.unstable_requestPaint,gt=r.unstable_now,Ul=r.unstable_getCurrentPriorityLevel,et=r.unstable_ImmediatePriority,ca=r.unstable_UserBlockingPriority,_t=r.unstable_NormalPriority,jt=r.unstable_LowPriority,Ja=r.unstable_IdlePriority,Ll=r.log,ql=r.unstable_setDisableYieldValue,oa=null,tt=null;function Ct(e){if(typeof Ll=="function"&&ql(e),tt&&typeof tt.setStrictMode=="function")try{tt.setStrictMode(oa,e)}catch{}}var at=Math.clz32?Math.clz32:mn,yn=Math.log,ir=Math.LN2;function mn(e){return e>>>=0,e===0?32:31-(yn(e)/ir|0)|0}var $a=256,Ra=4194304;function Re(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ea(e,t,a){var l=e.pendingLanes;if(l===0)return 0;var n=0,i=e.suspendedLanes,s=e.pingedLanes;e=e.warmLanes;var v=l&134217727;return v!==0?(l=v&~i,l!==0?n=Re(l):(s&=v,s!==0?n=Re(s):a||(a=v&~e,a!==0&&(n=Re(a))))):(v=l&~i,v!==0?n=Re(v):s!==0?n=Re(s):a||(a=l&~e,a!==0&&(n=Re(a)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,a=t&-t,i>=a||i===32&&(a&4194048)!==0)?t:n}function Ta(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function bn(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Sn(){var e=$a;return $a<<=1,($a&4194048)===0&&($a=256),e}function jl(){var e=Ra;return Ra<<=1,(Ra&62914560)===0&&(Ra=4194304),e}function Yl(e){for(var t=[],a=0;31>a;a++)t.push(e);return t}function ee(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Le(e,t,a,l,n,i){var s=e.pendingLanes;e.pendingLanes=a,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=a,e.entangledLanes&=a,e.errorRecoveryDisabledLanes&=a,e.shellSuspendCounter=0;var v=e.entanglements,m=e.expirationTimes,O=e.hiddenUpdates;for(a=s&~a;0)":-1n||m[l]!==O[n]){var N=` -`+m[l].replace(" at new "," at ");return e.displayName&&N.includes("")&&(N=N.replace("",e.displayName)),N}while(1<=l&&0<=n);break}}}finally{Rn=!1,Error.prepareStackTrace=a}return(a=e?e.displayName||e.name:"")?Aa(a):""}function ji(e){switch(e.tag){case 26:case 27:case 5:return Aa(e.type);case 16:return Aa("Lazy");case 13:return Aa("Suspense");case 19:return Aa("SuspenseList");case 0:case 15:return En(e.type,!1);case 11:return En(e.type.render,!1);case 1:return En(e.type,!0);case 31:return Aa("Activity");default:return""}}function dr(e){try{var t="";do t+=ji(e),e=e.return;while(e);return t}catch(a){return` -Error generating stack: `+a.message+` -`+a.stack}}function vt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function vr(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Yi(e){var t=vr(e)?"checked":"value",a=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),l=""+e[t];if(!e.hasOwnProperty(t)&&typeof a<"u"&&typeof a.get=="function"&&typeof a.set=="function"){var n=a.get,i=a.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return n.call(this)},set:function(s){l=""+s,i.call(this,s)}}),Object.defineProperty(e,t,{enumerable:a.enumerable}),{getValue:function(){return l},setValue:function(s){l=""+s},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Vl(e){e._valueTracker||(e._valueTracker=Yi(e))}function hr(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var a=t.getValue(),l="";return e&&(l=vr(e)?e.checked?"true":"false":e.value),e=l,e!==a?(t.setValue(e),!0):!1}function kl(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Gi=/[\n"\\]/g;function ht(e){return e.replace(Gi,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Ql(e,t,a,l,n,i,s,v){e.name="",s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.type=s:e.removeAttribute("type"),t!=null?s==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+vt(t)):e.value!==""+vt(t)&&(e.value=""+vt(t)):s!=="submit"&&s!=="reset"||e.removeAttribute("value"),t!=null?Tn(e,s,vt(t)):a!=null?Tn(e,s,vt(a)):l!=null&&e.removeAttribute("value"),n==null&&i!=null&&(e.defaultChecked=!!i),n!=null&&(e.checked=n&&typeof n!="function"&&typeof n!="symbol"),v!=null&&typeof v!="function"&&typeof v!="symbol"&&typeof v!="boolean"?e.name=""+vt(v):e.removeAttribute("name")}function yr(e,t,a,l,n,i,s,v){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||a!=null){if(!(i!=="submit"&&i!=="reset"||t!=null))return;a=a!=null?""+vt(a):"",t=t!=null?""+vt(t):a,v||t===e.value||(e.value=t),e.defaultValue=t}l=l??n,l=typeof l!="function"&&typeof l!="symbol"&&!!l,e.checked=v?e.checked:!!l,e.defaultChecked=!!l,s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(e.name=s)}function Tn(e,t,a){t==="number"&&kl(e.ownerDocument)===e||e.defaultValue===""+a||(e.defaultValue=""+a)}function _a(e,t,a,l){if(e=e.options,t){t={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Er=!1;if(Mt)try{var il={};Object.defineProperty(il,"passive",{get:function(){Er=!0}}),window.addEventListener("test",il,il),window.removeEventListener("test",il,il)}catch{Er=!1}var It=null,Cn=null,Zl=null;function Tr(){if(Zl)return Zl;var e,t=Cn,a=t.length,l,n="value"in It?It.value:It.textContent,i=n.length;for(e=0;e=Ar),Qs=" ",Ks=!1;function Zs(e,t){switch(e){case"keyup":return Ib.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Ws(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Mn=!1;function eS(e,t){switch(e){case"compositionend":return Ws(t);case"keypress":return t.which!==32?null:(Ks=!0,Qs);case"textInput":return e=t.data,e===Qs&&Ks?null:e;default:return null}}function tS(e,t){if(Mn)return e==="compositionend"||!xc&&Zs(e,t)?(e=Tr(),Zl=Cn=It=null,Mn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:a,offset:t-e};e=l}e:{for(;a;){if(a.nextSibling){a=a.nextSibling;break e}a=a.parentNode}a=void 0}a=ad(a)}}function nd(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?nd(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function rd(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=kl(e.document);t instanceof e.HTMLIFrameElement;){try{var a=typeof t.contentWindow.location.href=="string"}catch{a=!1}if(a)e=t.contentWindow;else break;t=kl(e.document)}return t}function _c(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var oS=Mt&&"documentMode"in document&&11>=document.documentMode,Dn=null,Cc=null,Dr=null,Mc=!1;function id(e,t,a){var l=a.window===a?a.document:a.nodeType===9?a:a.ownerDocument;Mc||Dn==null||Dn!==kl(l)||(l=Dn,"selectionStart"in l&&_c(l)?l={start:l.selectionStart,end:l.selectionEnd}:(l=(l.ownerDocument&&l.ownerDocument.defaultView||window).getSelection(),l={anchorNode:l.anchorNode,anchorOffset:l.anchorOffset,focusNode:l.focusNode,focusOffset:l.focusOffset}),Dr&&Mr(Dr,l)||(Dr=l,l=Bu(Cc,"onSelect"),0>=s,n-=s,za=1<<32-at(t)+n|a<i?i:8;var s=D.T,v={};D.T=v,yo(e,!1,t,a);try{var m=n(),O=D.S;if(O!==null&&O(v,m),m!==null&&typeof m=="object"&&typeof m.then=="function"){var N=SS(m,l);Qr(e,t,N,Ht(e))}else Qr(e,t,l,Ht(e))}catch(U){Qr(e,t,{then:function(){},status:"rejected",reason:U},Ht())}finally{X.p=i,D.T=s}}function TS(){}function vo(e,t,a,l){if(e.tag!==5)throw Error(c(476));var n=uv(e).queue;iv(e,n,t,K,a===null?TS:function(){return cv(e),a(l)})}function uv(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:K,baseState:K,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:K},next:null};var a={};return t.next={memoizedState:a,baseState:a,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:a},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function cv(e){var t=uv(e).next.queue;Qr(e,t,{},Ht())}function ho(){return ct(oi)}function ov(){return Ze().memoizedState}function fv(){return Ze().memoizedState}function xS(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var a=Ht();e=ol(a);var l=fl(t,e,a);l!==null&&(Ut(l,t,a),jr(l,t,a)),t={cache:Vc()},e.payload=t;return}t=t.return}}function OS(e,t,a){var l=Ht();a={lane:l,revertLane:0,action:a,hasEagerState:!1,eagerState:null,next:null},hu(e)?dv(t,a):(a=Nc(e,t,a,l),a!==null&&(Ut(a,e,l),vv(a,t,l)))}function sv(e,t,a){var l=Ht();Qr(e,t,a,l)}function Qr(e,t,a,l){var n={lane:l,revertLane:0,action:a,hasEagerState:!1,eagerState:null,next:null};if(hu(e))dv(t,n);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var s=t.lastRenderedState,v=i(s,a);if(n.hasEagerState=!0,n.eagerState=v,Dt(v,s))return $i(e,t,n,0),Be===null&&Ji(),!1}catch{}if(a=Nc(e,t,n,l),a!==null)return Ut(a,e,l),vv(a,t,l),!0}return!1}function yo(e,t,a,l){if(l={lane:2,revertLane:Zo(),action:l,hasEagerState:!1,eagerState:null,next:null},hu(e)){if(t)throw Error(c(479))}else t=Nc(e,a,l,2),t!==null&&Ut(t,e,2)}function hu(e){var t=e.alternate;return e===se||t!==null&&t===se}function dv(e,t){Yn=cu=!0;var a=e.pending;a===null?t.next=t:(t.next=a.next,a.next=t),e.pending=t}function vv(e,t,a){if((a&4194048)!==0){var l=t.lanes;l&=e.pendingLanes,a|=l,t.lanes=a,Ia(e,a)}}var yu={readContext:ct,use:fu,useCallback:Ve,useContext:Ve,useEffect:Ve,useImperativeHandle:Ve,useLayoutEffect:Ve,useInsertionEffect:Ve,useMemo:Ve,useReducer:Ve,useRef:Ve,useState:Ve,useDebugValue:Ve,useDeferredValue:Ve,useTransition:Ve,useSyncExternalStore:Ve,useId:Ve,useHostTransitionStatus:Ve,useFormState:Ve,useActionState:Ve,useOptimistic:Ve,useMemoCache:Ve,useCacheRefresh:Ve},hv={readContext:ct,use:fu,useCallback:function(e,t){return Rt().memoizedState=[e,t===void 0?null:t],e},useContext:ct,useEffect:$d,useImperativeHandle:function(e,t,a){a=a!=null?a.concat([e]):null,vu(4194308,4,tv.bind(null,t,e),a)},useLayoutEffect:function(e,t){return vu(4194308,4,e,t)},useInsertionEffect:function(e,t){vu(4,2,e,t)},useMemo:function(e,t){var a=Rt();t=t===void 0?null:t;var l=e();if(rn){Ct(!0);try{e()}finally{Ct(!1)}}return a.memoizedState=[l,t],l},useReducer:function(e,t,a){var l=Rt();if(a!==void 0){var n=a(t);if(rn){Ct(!0);try{a(t)}finally{Ct(!1)}}}else n=t;return l.memoizedState=l.baseState=n,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},l.queue=e,e=e.dispatch=OS.bind(null,se,e),[l.memoizedState,e]},useRef:function(e){var t=Rt();return e={current:e},t.memoizedState=e},useState:function(e){e=co(e);var t=e.queue,a=sv.bind(null,se,t);return t.dispatch=a,[e.memoizedState,a]},useDebugValue:fo,useDeferredValue:function(e,t){var a=Rt();return so(a,e,t)},useTransition:function(){var e=co(!1);return e=iv.bind(null,se,e.queue,!0,!1),Rt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,a){var l=se,n=Rt();if(Ee){if(a===void 0)throw Error(c(407));a=a()}else{if(a=t(),Be===null)throw Error(c(349));(Se&124)!==0||Hd(l,t,a)}n.memoizedState=a;var i={value:a,getSnapshot:t};return n.queue=i,$d(Ld.bind(null,l,i,e),[e]),l.flags|=2048,Xn(9,du(),Ud.bind(null,l,i,a,t),null),a},useId:function(){var e=Rt(),t=Be.identifierPrefix;if(Ee){var a=Na,l=za;a=(l&~(1<<32-at(l)-1)).toString(32)+a,t="«"+t+"R"+a,a=ou++,0le?(Fe=I,I=null):Fe=I.sibling;var ge=_(E,I,T[le],B);if(ge===null){I===null&&(I=Fe);break}e&&I&&ge.alternate===null&&t(E,I),p=i(ge,p,le),ve===null?P=ge:ve.sibling=ge,ve=ge,I=Fe}if(le===T.length)return a(E,I),Ee&&Fl(E,le),P;if(I===null){for(;lele?(Fe=I,I=null):Fe=I.sibling;var _l=_(E,I,ge.value,B);if(_l===null){I===null&&(I=Fe);break}e&&I&&_l.alternate===null&&t(E,I),p=i(_l,p,le),ve===null?P=_l:ve.sibling=_l,ve=_l,I=Fe}if(ge.done)return a(E,I),Ee&&Fl(E,le),P;if(I===null){for(;!ge.done;le++,ge=T.next())ge=U(E,ge.value,B),ge!==null&&(p=i(ge,p,le),ve===null?P=ge:ve.sibling=ge,ve=ge);return Ee&&Fl(E,le),P}for(I=l(I);!ge.done;le++,ge=T.next())ge=C(I,E,le,ge.value,B),ge!==null&&(e&&ge.alternate!==null&&I.delete(ge.key===null?le:ge.key),p=i(ge,p,le),ve===null?P=ge:ve.sibling=ge,ve=ge);return e&&I.forEach(function(_g){return t(E,_g)}),Ee&&Fl(E,le),P}function Me(E,p,T,B){if(typeof T=="object"&&T!==null&&T.type===L&&T.key===null&&(T=T.props.children),typeof T=="object"&&T!==null){switch(T.$$typeof){case M:e:{for(var P=T.key;p!==null;){if(p.key===P){if(P=T.type,P===L){if(p.tag===7){a(E,p.sibling),B=n(p,T.props.children),B.return=E,E=B;break e}}else if(p.elementType===P||typeof P=="object"&&P!==null&&P.$$typeof===ie&&mv(P)===p.type){a(E,p.sibling),B=n(p,T.props),Zr(B,T),B.return=E,E=B;break e}a(E,p);break}else t(E,p);p=p.sibling}T.type===L?(B=$l(T.props.children,E.mode,B,T.key),B.return=E,E=B):(B=Fi(T.type,T.key,T.props,null,E.mode,B),Zr(B,T),B.return=E,E=B)}return s(E);case q:e:{for(P=T.key;p!==null;){if(p.key===P)if(p.tag===4&&p.stateNode.containerInfo===T.containerInfo&&p.stateNode.implementation===T.implementation){a(E,p.sibling),B=n(p,T.children||[]),B.return=E,E=B;break e}else{a(E,p);break}else t(E,p);p=p.sibling}B=Uc(T,E.mode,B),B.return=E,E=B}return s(E);case ie:return P=T._init,T=P(T._payload),Me(E,p,T,B)}if(Ae(T))return re(E,p,T,B);if(pe(T)){if(P=pe(T),typeof P!="function")throw Error(c(150));return T=P.call(T),ae(E,p,T,B)}if(typeof T.then=="function")return Me(E,p,mu(T),B);if(T.$$typeof===Y)return Me(E,p,lu(E,T),B);bu(E,T)}return typeof T=="string"&&T!==""||typeof T=="number"||typeof T=="bigint"?(T=""+T,p!==null&&p.tag===6?(a(E,p.sibling),B=n(p,T),B.return=E,E=B):(a(E,p),B=Hc(T,E.mode,B),B.return=E,E=B),s(E)):a(E,p)}return function(E,p,T,B){try{Kr=0;var P=Me(E,p,T,B);return Vn=null,P}catch(I){if(I===Lr||I===ru)throw I;var ve=wt(29,I,null,E.mode);return ve.lanes=B,ve.return=E,ve}}}var kn=bv(!0),Sv=bv(!1),Kt=H(null),da=null;function dl(e){var t=e.alternate;V(Pe,Pe.current&1),V(Kt,e),da===null&&(t===null||jn.current!==null||t.memoizedState!==null)&&(da=e)}function gv(e){if(e.tag===22){if(V(Pe,Pe.current),V(Kt,e),da===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(da=e)}}else vl()}function vl(){V(Pe,Pe.current),V(Kt,Kt.current)}function La(e){k(Kt),da===e&&(da=null),k(Pe)}var Pe=H(0);function Su(e){for(var t=e;t!==null;){if(t.tag===13){var a=t.memoizedState;if(a!==null&&(a=a.dehydrated,a===null||a.data==="$?"||rf(a)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function mo(e,t,a,l){t=e.memoizedState,a=a(l,t),a=a==null?t:R({},t,a),e.memoizedState=a,e.lanes===0&&(e.updateQueue.baseState=a)}var bo={enqueueSetState:function(e,t,a){e=e._reactInternals;var l=Ht(),n=ol(l);n.payload=t,a!=null&&(n.callback=a),t=fl(e,n,l),t!==null&&(Ut(t,e,l),jr(t,e,l))},enqueueReplaceState:function(e,t,a){e=e._reactInternals;var l=Ht(),n=ol(l);n.tag=1,n.payload=t,a!=null&&(n.callback=a),t=fl(e,n,l),t!==null&&(Ut(t,e,l),jr(t,e,l))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var a=Ht(),l=ol(a);l.tag=2,t!=null&&(l.callback=t),t=fl(e,l,a),t!==null&&(Ut(t,e,a),jr(t,e,a))}};function pv(e,t,a,l,n,i,s){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(l,i,s):t.prototype&&t.prototype.isPureReactComponent?!Mr(a,l)||!Mr(n,i):!0}function Rv(e,t,a,l){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(a,l),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(a,l),t.state!==e&&bo.enqueueReplaceState(t,t.state,null)}function un(e,t){var a=t;if("ref"in t){a={};for(var l in t)l!=="ref"&&(a[l]=t[l])}if(e=e.defaultProps){a===t&&(a=R({},a));for(var n in e)a[n]===void 0&&(a[n]=e[n])}return a}var gu=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Ev(e){gu(e)}function Tv(e){console.error(e)}function xv(e){gu(e)}function pu(e,t){try{var a=e.onUncaughtError;a(t.value,{componentStack:t.stack})}catch(l){setTimeout(function(){throw l})}}function Ov(e,t,a){try{var l=e.onCaughtError;l(a.value,{componentStack:a.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(n){setTimeout(function(){throw n})}}function So(e,t,a){return a=ol(a),a.tag=3,a.payload={element:null},a.callback=function(){pu(e,t)},a}function Av(e){return e=ol(e),e.tag=3,e}function _v(e,t,a,l){var n=a.type.getDerivedStateFromError;if(typeof n=="function"){var i=l.value;e.payload=function(){return n(i)},e.callback=function(){Ov(t,a,l)}}var s=a.stateNode;s!==null&&typeof s.componentDidCatch=="function"&&(e.callback=function(){Ov(t,a,l),typeof n!="function"&&(gl===null?gl=new Set([this]):gl.add(this));var v=l.stack;this.componentDidCatch(l.value,{componentStack:v!==null?v:""})})}function _S(e,t,a,l,n){if(a.flags|=32768,l!==null&&typeof l=="object"&&typeof l.then=="function"){if(t=a.alternate,t!==null&&Br(t,a,n,!0),a=Kt.current,a!==null){switch(a.tag){case 13:return da===null?Xo():a.alternate===null&&Ge===0&&(Ge=3),a.flags&=-257,a.flags|=65536,a.lanes=n,l===Kc?a.flags|=16384:(t=a.updateQueue,t===null?a.updateQueue=new Set([l]):t.add(l),ko(e,l,n)),!1;case 22:return a.flags|=65536,l===Kc?a.flags|=16384:(t=a.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([l])},a.updateQueue=t):(a=t.retryQueue,a===null?t.retryQueue=new Set([l]):a.add(l)),ko(e,l,n)),!1}throw Error(c(435,a.tag))}return ko(e,l,n),Xo(),!1}if(Ee)return t=Kt.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=n,l!==jc&&(e=Error(c(422),{cause:l}),Nr(Xt(e,a)))):(l!==jc&&(t=Error(c(423),{cause:l}),Nr(Xt(t,a))),e=e.current.alternate,e.flags|=65536,n&=-n,e.lanes|=n,l=Xt(l,a),n=So(e.stateNode,l,n),Pc(e,n),Ge!==4&&(Ge=2)),!1;var i=Error(c(520),{cause:l});if(i=Xt(i,a),ei===null?ei=[i]:ei.push(i),Ge!==4&&(Ge=2),t===null)return!0;l=Xt(l,a),a=t;do{switch(a.tag){case 3:return a.flags|=65536,e=n&-n,a.lanes|=e,e=So(a.stateNode,l,e),Pc(a,e),!1;case 1:if(t=a.type,i=a.stateNode,(a.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||i!==null&&typeof i.componentDidCatch=="function"&&(gl===null||!gl.has(i))))return a.flags|=65536,n&=-n,a.lanes|=n,n=Av(n),_v(n,e,a,l),Pc(a,n),!1}a=a.return}while(a!==null);return!1}var Cv=Error(c(461)),$e=!1;function nt(e,t,a,l){t.child=e===null?Sv(t,null,a,l):kn(t,e.child,a,l)}function Mv(e,t,a,l,n){a=a.render;var i=t.ref;if("ref"in l){var s={};for(var v in l)v!=="ref"&&(s[v]=l[v])}else s=l;return ln(t),l=eo(e,t,a,s,i,n),v=to(),e!==null&&!$e?(ao(e,t,n),qa(e,t,n)):(Ee&&v&&Lc(t),t.flags|=1,nt(e,t,l,n),t.child)}function Dv(e,t,a,l,n){if(e===null){var i=a.type;return typeof i=="function"&&!Bc(i)&&i.defaultProps===void 0&&a.compare===null?(t.tag=15,t.type=i,wv(e,t,i,l,n)):(e=Fi(a.type,null,l,t,t.mode,n),e.ref=t.ref,e.return=t,t.child=e)}if(i=e.child,!Ao(e,n)){var s=i.memoizedProps;if(a=a.compare,a=a!==null?a:Mr,a(s,l)&&e.ref===t.ref)return qa(e,t,n)}return t.flags|=1,e=wa(i,l),e.ref=t.ref,e.return=t,t.child=e}function wv(e,t,a,l,n){if(e!==null){var i=e.memoizedProps;if(Mr(i,l)&&e.ref===t.ref)if($e=!1,t.pendingProps=l=i,Ao(e,n))(e.flags&131072)!==0&&($e=!0);else return t.lanes=e.lanes,qa(e,t,n)}return go(e,t,a,l,n)}function zv(e,t,a){var l=t.pendingProps,n=l.children,i=e!==null?e.memoizedState:null;if(l.mode==="hidden"){if((t.flags&128)!==0){if(l=i!==null?i.baseLanes|a:a,e!==null){for(n=t.child=e.child,i=0;n!==null;)i=i|n.lanes|n.childLanes,n=n.sibling;t.childLanes=i&~l}else t.childLanes=0,t.child=null;return Nv(e,t,l,a)}if((a&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&nu(t,i!==null?i.cachePool:null),i!==null?wd(t,i):$c(),gv(t);else return t.lanes=t.childLanes=536870912,Nv(e,t,i!==null?i.baseLanes|a:a,a)}else i!==null?(nu(t,i.cachePool),wd(t,i),vl(),t.memoizedState=null):(e!==null&&nu(t,null),$c(),vl());return nt(e,t,n,a),t.child}function Nv(e,t,a,l){var n=Qc();return n=n===null?null:{parent:We._currentValue,pool:n},t.memoizedState={baseLanes:a,cachePool:n},e!==null&&nu(t,null),$c(),gv(t),e!==null&&Br(e,t,l,!0),null}function Ru(e,t){var a=t.ref;if(a===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof a!="function"&&typeof a!="object")throw Error(c(284));(e===null||e.ref!==a)&&(t.flags|=4194816)}}function go(e,t,a,l,n){return ln(t),a=eo(e,t,a,l,void 0,n),l=to(),e!==null&&!$e?(ao(e,t,n),qa(e,t,n)):(Ee&&l&&Lc(t),t.flags|=1,nt(e,t,a,n),t.child)}function Bv(e,t,a,l,n,i){return ln(t),t.updateQueue=null,a=Nd(t,l,a,n),zd(e),l=to(),e!==null&&!$e?(ao(e,t,i),qa(e,t,i)):(Ee&&l&&Lc(t),t.flags|=1,nt(e,t,a,i),t.child)}function Hv(e,t,a,l,n){if(ln(t),t.stateNode===null){var i=Bn,s=a.contextType;typeof s=="object"&&s!==null&&(i=ct(s)),i=new a(l,i),t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,i.updater=bo,t.stateNode=i,i._reactInternals=t,i=t.stateNode,i.props=l,i.state=t.memoizedState,i.refs={},Zc(t),s=a.contextType,i.context=typeof s=="object"&&s!==null?ct(s):Bn,i.state=t.memoizedState,s=a.getDerivedStateFromProps,typeof s=="function"&&(mo(t,a,s,l),i.state=t.memoizedState),typeof a.getDerivedStateFromProps=="function"||typeof i.getSnapshotBeforeUpdate=="function"||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(s=i.state,typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount(),s!==i.state&&bo.enqueueReplaceState(i,i.state,null),Gr(t,l,i,n),Yr(),i.state=t.memoizedState),typeof i.componentDidMount=="function"&&(t.flags|=4194308),l=!0}else if(e===null){i=t.stateNode;var v=t.memoizedProps,m=un(a,v);i.props=m;var O=i.context,N=a.contextType;s=Bn,typeof N=="object"&&N!==null&&(s=ct(N));var U=a.getDerivedStateFromProps;N=typeof U=="function"||typeof i.getSnapshotBeforeUpdate=="function",v=t.pendingProps!==v,N||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(v||O!==s)&&Rv(t,i,l,s),cl=!1;var _=t.memoizedState;i.state=_,Gr(t,l,i,n),Yr(),O=t.memoizedState,v||_!==O||cl?(typeof U=="function"&&(mo(t,a,U,l),O=t.memoizedState),(m=cl||pv(t,a,m,l,_,O,s))?(N||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=l,t.memoizedState=O),i.props=l,i.state=O,i.context=s,l=m):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),l=!1)}else{i=t.stateNode,Wc(e,t),s=t.memoizedProps,N=un(a,s),i.props=N,U=t.pendingProps,_=i.context,O=a.contextType,m=Bn,typeof O=="object"&&O!==null&&(m=ct(O)),v=a.getDerivedStateFromProps,(O=typeof v=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s!==U||_!==m)&&Rv(t,i,l,m),cl=!1,_=t.memoizedState,i.state=_,Gr(t,l,i,n),Yr();var C=t.memoizedState;s!==U||_!==C||cl||e!==null&&e.dependencies!==null&&au(e.dependencies)?(typeof v=="function"&&(mo(t,a,v,l),C=t.memoizedState),(N=cl||pv(t,a,N,l,_,C,m)||e!==null&&e.dependencies!==null&&au(e.dependencies))?(O||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(l,C,m),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(l,C,m)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=1024),t.memoizedProps=l,t.memoizedState=C),i.props=l,i.state=C,i.context=m,l=N):(typeof i.componentDidUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||s===e.memoizedProps&&_===e.memoizedState||(t.flags|=1024),l=!1)}return i=l,Ru(e,t),l=(t.flags&128)!==0,i||l?(i=t.stateNode,a=l&&typeof a.getDerivedStateFromError!="function"?null:i.render(),t.flags|=1,e!==null&&l?(t.child=kn(t,e.child,null,n),t.child=kn(t,null,a,n)):nt(e,t,a,n),t.memoizedState=i.state,e=t.child):e=qa(e,t,n),e}function Uv(e,t,a,l){return zr(),t.flags|=256,nt(e,t,a,l),t.child}var po={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ro(e){return{baseLanes:e,cachePool:Td()}}function Eo(e,t,a){return e=e!==null?e.childLanes&~a:0,t&&(e|=Zt),e}function Lv(e,t,a){var l=t.pendingProps,n=!1,i=(t.flags&128)!==0,s;if((s=i)||(s=e!==null&&e.memoizedState===null?!1:(Pe.current&2)!==0),s&&(n=!0,t.flags&=-129),s=(t.flags&32)!==0,t.flags&=-33,e===null){if(Ee){if(n?dl(t):vl(),Ee){var v=Ye,m;if(m=v){e:{for(m=v,v=sa;m.nodeType!==8;){if(!v){v=null;break e}if(m=ta(m.nextSibling),m===null){v=null;break e}}v=m}v!==null?(t.memoizedState={dehydrated:v,treeContext:Il!==null?{id:za,overflow:Na}:null,retryLane:536870912,hydrationErrors:null},m=wt(18,null,null,0),m.stateNode=v,m.return=t,t.child=m,yt=t,Ye=null,m=!0):m=!1}m||tn(t)}if(v=t.memoizedState,v!==null&&(v=v.dehydrated,v!==null))return rf(v)?t.lanes=32:t.lanes=536870912,null;La(t)}return v=l.children,l=l.fallback,n?(vl(),n=t.mode,v=Eu({mode:"hidden",children:v},n),l=$l(l,n,a,null),v.return=t,l.return=t,v.sibling=l,t.child=v,n=t.child,n.memoizedState=Ro(a),n.childLanes=Eo(e,s,a),t.memoizedState=po,l):(dl(t),To(t,v))}if(m=e.memoizedState,m!==null&&(v=m.dehydrated,v!==null)){if(i)t.flags&256?(dl(t),t.flags&=-257,t=xo(e,t,a)):t.memoizedState!==null?(vl(),t.child=e.child,t.flags|=128,t=null):(vl(),n=l.fallback,v=t.mode,l=Eu({mode:"visible",children:l.children},v),n=$l(n,v,a,null),n.flags|=2,l.return=t,n.return=t,l.sibling=n,t.child=l,kn(t,e.child,null,a),l=t.child,l.memoizedState=Ro(a),l.childLanes=Eo(e,s,a),t.memoizedState=po,t=n);else if(dl(t),rf(v)){if(s=v.nextSibling&&v.nextSibling.dataset,s)var O=s.dgst;s=O,l=Error(c(419)),l.stack="",l.digest=s,Nr({value:l,source:null,stack:null}),t=xo(e,t,a)}else if($e||Br(e,t,a,!1),s=(a&e.childLanes)!==0,$e||s){if(s=Be,s!==null&&(l=a&-a,l=(l&42)!==0?1:Yt(l),l=(l&(s.suspendedLanes|a))!==0?0:l,l!==0&&l!==m.retryLane))throw m.retryLane=l,Nn(e,l),Ut(s,e,l),Cv;v.data==="$?"||Xo(),t=xo(e,t,a)}else v.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=m.treeContext,Ye=ta(v.nextSibling),yt=t,Ee=!0,en=null,sa=!1,e!==null&&(kt[Qt++]=za,kt[Qt++]=Na,kt[Qt++]=Il,za=e.id,Na=e.overflow,Il=t),t=To(t,l.children),t.flags|=4096);return t}return n?(vl(),n=l.fallback,v=t.mode,m=e.child,O=m.sibling,l=wa(m,{mode:"hidden",children:l.children}),l.subtreeFlags=m.subtreeFlags&65011712,O!==null?n=wa(O,n):(n=$l(n,v,a,null),n.flags|=2),n.return=t,l.return=t,l.sibling=n,t.child=l,l=n,n=t.child,v=e.child.memoizedState,v===null?v=Ro(a):(m=v.cachePool,m!==null?(O=We._currentValue,m=m.parent!==O?{parent:O,pool:O}:m):m=Td(),v={baseLanes:v.baseLanes|a,cachePool:m}),n.memoizedState=v,n.childLanes=Eo(e,s,a),t.memoizedState=po,l):(dl(t),a=e.child,e=a.sibling,a=wa(a,{mode:"visible",children:l.children}),a.return=t,a.sibling=null,e!==null&&(s=t.deletions,s===null?(t.deletions=[e],t.flags|=16):s.push(e)),t.child=a,t.memoizedState=null,a)}function To(e,t){return t=Eu({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Eu(e,t){return e=wt(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function xo(e,t,a){return kn(t,e.child,null,a),e=To(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function qv(e,t,a){e.lanes|=t;var l=e.alternate;l!==null&&(l.lanes|=t),Gc(e.return,t,a)}function Oo(e,t,a,l,n){var i=e.memoizedState;i===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:l,tail:a,tailMode:n}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=l,i.tail=a,i.tailMode=n)}function jv(e,t,a){var l=t.pendingProps,n=l.revealOrder,i=l.tail;if(nt(e,t,l.children,a),l=Pe.current,(l&2)!==0)l=l&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&qv(e,a,t);else if(e.tag===19)qv(e,a,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}l&=1}switch(V(Pe,l),n){case"forwards":for(a=t.child,n=null;a!==null;)e=a.alternate,e!==null&&Su(e)===null&&(n=a),a=a.sibling;a=n,a===null?(n=t.child,t.child=null):(n=a.sibling,a.sibling=null),Oo(t,!1,n,a,i);break;case"backwards":for(a=null,n=t.child,t.child=null;n!==null;){if(e=n.alternate,e!==null&&Su(e)===null){t.child=n;break}e=n.sibling,n.sibling=a,a=n,n=e}Oo(t,!0,a,null,i);break;case"together":Oo(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function qa(e,t,a){if(e!==null&&(t.dependencies=e.dependencies),Sl|=t.lanes,(a&t.childLanes)===0)if(e!==null){if(Br(e,t,a,!1),(a&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(c(153));if(t.child!==null){for(e=t.child,a=wa(e,e.pendingProps),t.child=a,a.return=t;e.sibling!==null;)e=e.sibling,a=a.sibling=wa(e,e.pendingProps),a.return=t;a.sibling=null}return t.child}function Ao(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&au(e)))}function CS(e,t,a){switch(t.tag){case 3:ce(t,t.stateNode.containerInfo),ul(t,We,e.memoizedState.cache),zr();break;case 27:case 5:Za(t);break;case 4:ce(t,t.stateNode.containerInfo);break;case 10:ul(t,t.type,t.memoizedProps.value);break;case 13:var l=t.memoizedState;if(l!==null)return l.dehydrated!==null?(dl(t),t.flags|=128,null):(a&t.child.childLanes)!==0?Lv(e,t,a):(dl(t),e=qa(e,t,a),e!==null?e.sibling:null);dl(t);break;case 19:var n=(e.flags&128)!==0;if(l=(a&t.childLanes)!==0,l||(Br(e,t,a,!1),l=(a&t.childLanes)!==0),n){if(l)return jv(e,t,a);t.flags|=128}if(n=t.memoizedState,n!==null&&(n.rendering=null,n.tail=null,n.lastEffect=null),V(Pe,Pe.current),l)break;return null;case 22:case 23:return t.lanes=0,zv(e,t,a);case 24:ul(t,We,e.memoizedState.cache)}return qa(e,t,a)}function Yv(e,t,a){if(e!==null)if(e.memoizedProps!==t.pendingProps)$e=!0;else{if(!Ao(e,a)&&(t.flags&128)===0)return $e=!1,CS(e,t,a);$e=(e.flags&131072)!==0}else $e=!1,Ee&&(t.flags&1048576)!==0&&md(t,tu,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var l=t.elementType,n=l._init;if(l=n(l._payload),t.type=l,typeof l=="function")Bc(l)?(e=un(l,e),t.tag=1,t=Hv(null,t,l,e,a)):(t.tag=0,t=go(null,t,l,e,a));else{if(l!=null){if(n=l.$$typeof,n===J){t.tag=11,t=Mv(null,t,l,e,a);break e}else if(n===ne){t.tag=14,t=Dv(null,t,l,e,a);break e}}throw t=Qe(l)||l,Error(c(306,t,""))}}return t;case 0:return go(e,t,t.type,t.pendingProps,a);case 1:return l=t.type,n=un(l,t.pendingProps),Hv(e,t,l,n,a);case 3:e:{if(ce(t,t.stateNode.containerInfo),e===null)throw Error(c(387));l=t.pendingProps;var i=t.memoizedState;n=i.element,Wc(e,t),Gr(t,l,null,a);var s=t.memoizedState;if(l=s.cache,ul(t,We,l),l!==i.cache&&Xc(t,[We],a,!0),Yr(),l=s.element,i.isDehydrated)if(i={element:l,isDehydrated:!1,cache:s.cache},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){t=Uv(e,t,l,a);break e}else if(l!==n){n=Xt(Error(c(424)),t),Nr(n),t=Uv(e,t,l,a);break e}else for(e=t.stateNode.containerInfo,e.nodeType===9?e=e.body:e=e.nodeName==="HTML"?e.ownerDocument.body:e,Ye=ta(e.firstChild),yt=t,Ee=!0,en=null,sa=!0,a=Sv(t,null,l,a),t.child=a;a;)a.flags=a.flags&-3|4096,a=a.sibling;else{if(zr(),l===n){t=qa(e,t,a);break e}nt(e,t,l,a)}t=t.child}return t;case 26:return Ru(e,t),e===null?(a=kh(t.type,null,t.pendingProps,null))?t.memoizedState=a:Ee||(a=t.type,e=t.pendingProps,l=Uu(te.current).createElement(a),l[Je]=t,l[lt]=e,it(l,a,e),He(l),t.stateNode=l):t.memoizedState=kh(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return Za(t),e===null&&Ee&&(l=t.stateNode=Gh(t.type,t.pendingProps,te.current),yt=t,sa=!0,n=Ye,El(t.type)?(uf=n,Ye=ta(l.firstChild)):Ye=n),nt(e,t,t.pendingProps.children,a),Ru(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&Ee&&((n=l=Ye)&&(l=ag(l,t.type,t.pendingProps,sa),l!==null?(t.stateNode=l,yt=t,Ye=ta(l.firstChild),sa=!1,n=!0):n=!1),n||tn(t)),Za(t),n=t.type,i=t.pendingProps,s=e!==null?e.memoizedProps:null,l=i.children,af(n,i)?l=null:s!==null&&af(n,s)&&(t.flags|=32),t.memoizedState!==null&&(n=eo(e,t,pS,null,null,a),oi._currentValue=n),Ru(e,t),nt(e,t,l,a),t.child;case 6:return e===null&&Ee&&((e=a=Ye)&&(a=lg(a,t.pendingProps,sa),a!==null?(t.stateNode=a,yt=t,Ye=null,e=!0):e=!1),e||tn(t)),null;case 13:return Lv(e,t,a);case 4:return ce(t,t.stateNode.containerInfo),l=t.pendingProps,e===null?t.child=kn(t,null,l,a):nt(e,t,l,a),t.child;case 11:return Mv(e,t,t.type,t.pendingProps,a);case 7:return nt(e,t,t.pendingProps,a),t.child;case 8:return nt(e,t,t.pendingProps.children,a),t.child;case 12:return nt(e,t,t.pendingProps.children,a),t.child;case 10:return l=t.pendingProps,ul(t,t.type,l.value),nt(e,t,l.children,a),t.child;case 9:return n=t.type._context,l=t.pendingProps.children,ln(t),n=ct(n),l=l(n),t.flags|=1,nt(e,t,l,a),t.child;case 14:return Dv(e,t,t.type,t.pendingProps,a);case 15:return wv(e,t,t.type,t.pendingProps,a);case 19:return jv(e,t,a);case 31:return l=t.pendingProps,a=t.mode,l={mode:l.mode,children:l.children},e===null?(a=Eu(l,a),a.ref=t.ref,t.child=a,a.return=t,t=a):(a=wa(e.child,l),a.ref=t.ref,t.child=a,a.return=t,t=a),t;case 22:return zv(e,t,a);case 24:return ln(t),l=ct(We),e===null?(n=Qc(),n===null&&(n=Be,i=Vc(),n.pooledCache=i,i.refCount++,i!==null&&(n.pooledCacheLanes|=a),n=i),t.memoizedState={parent:l,cache:n},Zc(t),ul(t,We,n)):((e.lanes&a)!==0&&(Wc(e,t),Gr(t,null,null,a),Yr()),n=e.memoizedState,i=t.memoizedState,n.parent!==l?(n={parent:l,cache:l},t.memoizedState=n,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=n),ul(t,We,l)):(l=i.cache,ul(t,We,l),l!==n.cache&&Xc(t,[We],a,!0))),nt(e,t,t.pendingProps.children,a),t.child;case 29:throw t.pendingProps}throw Error(c(156,t.tag))}function ja(e){e.flags|=4}function Gv(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!Ph(t)){if(t=Kt.current,t!==null&&((Se&4194048)===Se?da!==null:(Se&62914560)!==Se&&(Se&536870912)===0||t!==da))throw qr=Kc,xd;e.flags|=8192}}function Tu(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?jl():536870912,e.lanes|=t,Wn|=t)}function Wr(e,t){if(!Ee)switch(e.tailMode){case"hidden":t=e.tail;for(var a=null;t!==null;)t.alternate!==null&&(a=t),t=t.sibling;a===null?e.tail=null:a.sibling=null;break;case"collapsed":a=e.tail;for(var l=null;a!==null;)a.alternate!==null&&(l=a),a=a.sibling;l===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:l.sibling=null}}function qe(e){var t=e.alternate!==null&&e.alternate.child===e.child,a=0,l=0;if(t)for(var n=e.child;n!==null;)a|=n.lanes|n.childLanes,l|=n.subtreeFlags&65011712,l|=n.flags&65011712,n.return=e,n=n.sibling;else for(n=e.child;n!==null;)a|=n.lanes|n.childLanes,l|=n.subtreeFlags,l|=n.flags,n.return=e,n=n.sibling;return e.subtreeFlags|=l,e.childLanes=a,t}function MS(e,t,a){var l=t.pendingProps;switch(qc(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return qe(t),null;case 1:return qe(t),null;case 3:return a=t.stateNode,l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),Ha(We),At(),a.pendingContext&&(a.context=a.pendingContext,a.pendingContext=null),(e===null||e.child===null)&&(wr(t)?ja(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,gd())),qe(t),null;case 26:return a=t.memoizedState,e===null?(ja(t),a!==null?(qe(t),Gv(t,a)):(qe(t),t.flags&=-16777217)):a?a!==e.memoizedState?(ja(t),qe(t),Gv(t,a)):(qe(t),t.flags&=-16777217):(e.memoizedProps!==l&&ja(t),qe(t),t.flags&=-16777217),null;case 27:Wa(t),a=te.current;var n=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==l&&ja(t);else{if(!l){if(t.stateNode===null)throw Error(c(166));return qe(t),null}e=$.current,wr(t)?bd(t):(e=Gh(n,l,a),t.stateNode=e,ja(t))}return qe(t),null;case 5:if(Wa(t),a=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==l&&ja(t);else{if(!l){if(t.stateNode===null)throw Error(c(166));return qe(t),null}if(e=$.current,wr(t))bd(t);else{switch(n=Uu(te.current),e){case 1:e=n.createElementNS("http://www.w3.org/2000/svg",a);break;case 2:e=n.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;default:switch(a){case"svg":e=n.createElementNS("http://www.w3.org/2000/svg",a);break;case"math":e=n.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;case"script":e=n.createElement("div"),e.innerHTML=" - - - -

- - - \ No newline at end of file From af1a1e2a52fa69c021a20958dc96d34bbab4c883 Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Mon, 6 Apr 2026 08:00:30 -0400 Subject: [PATCH 4/4] Clean up --- docs/docs/installation.md | 8 + example/index.web.tsx | 14 +- example/package.json | 1 + example/src/App.tsx | 184 +-- example/src/AppWeb.tsx | 717 +++++----- example/src/tests/blob.ts | 164 +-- example/src/tests/constants.ts | 38 +- example/src/tests/dbsetup.ts | 542 ++++---- example/src/tests/hooks.ts | 485 ++++--- example/src/tests/index.ts | 20 +- example/src/tests/preparedStatements.ts | 200 +-- example/src/tests/queries.ts | 1621 +++++++++++------------ example/src/tests/reactive.ts | 648 ++++----- example/src/tests/storage.ts | 114 +- example/src/tests/utils.ts | 8 +- example/src/tests/web.ts | 164 +-- package.json | 257 ++-- src/functions.ts | 792 +++++------ src/functions.web.ts | 837 ++++++------ src/sqlite-wasm-optional.d.ts | 3 + src/types.ts | 522 ++++---- yarn.lock | 97 ++ 22 files changed, 3801 insertions(+), 3635 deletions(-) create mode 100644 src/sqlite-wasm-optional.d.ts diff --git a/docs/docs/installation.md b/docs/docs/installation.md index ecfd5304..e3d74b4b 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -23,6 +23,14 @@ This package runs on `iOS`, `Android`, `macOS` and `web`. Web support is async-only and uses the sqlite wasm worker API with OPFS persistence. +`@sqlite.org/sqlite-wasm` is an optional dependency. You only need to install it if you use op-sqlite on web. + +```bash +yarn add @sqlite.org/sqlite-wasm +``` + +If your app does not target web, you do not need to install this package. + Required runtime behavior on web: - Use `openAsync()` to open the database. diff --git a/example/index.web.tsx b/example/index.web.tsx index d466b7f2..7ec2d5f6 100644 --- a/example/index.web.tsx +++ b/example/index.web.tsx @@ -1,15 +1,15 @@ -import { AppRegistry } from 'react-native'; -import App from './src/AppWeb'; -import { name as appName } from './app.json'; +import { AppRegistry } from "react-native"; +import { name as appName } from "./app.json"; +import App from "./src/AppWeb"; AppRegistry.registerComponent(appName, () => App); -const rootTag = (globalThis as any).document?.getElementById('root'); +const rootTag = (globalThis as any).document?.getElementById("root"); if (!rootTag) { - throw new Error('Root element not found'); + throw new Error("Root element not found"); } AppRegistry.runApplication(appName, { - rootTag, - initialProps: {}, + rootTag, + initialProps: {}, }); diff --git a/example/package.json b/example/package.json index 3e99b4b9..a8cdae4e 100644 --- a/example/package.json +++ b/example/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@op-engineering/op-test": "^0.2.5", + "@sqlite.org/sqlite-wasm": "^3.51.2-build8", "chance": "^1.1.9", "clsx": "^2.0.0", "events": "^3.3.0", diff --git a/example/src/App.tsx b/example/src/App.tsx index 61eb4579..f542f0ab 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,108 +1,108 @@ -import {useEffect, useState} from 'react'; import { - displayResults, - runTests, - allTestsPassed, -} from '@op-engineering/op-test'; -import './tests'; // import all tests to register them -import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context'; + allTestsPassed, + displayResults, + runTests, +} from "@op-engineering/op-test"; +import { useEffect, useState } from "react"; +import "./tests"; // import all tests to register them // import {performanceTest} from './performance_test'; -import {StyleSheet, Text, View} from 'react-native'; +import { StyleSheet, Text, View } from "react-native"; +import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context"; // import {open} from '@op-engineering/op-sqlite'; export default function App() { - const [results, setResults] = useState(null); - const [perfResult, setPerfResult] = useState(0); - const [openTime, setOpenTime] = useState(0); + const [results, setResults] = useState(null); + const [perfResult, setPerfResult] = useState(0); + const [openTime, setOpenTime] = useState(0); - useEffect(() => { - console.log("App has started 🟢") - const work = async () => { - // let start = performance.now(); - // open({ - // name: 'dummyDb.sqlite', - // }); - // setOpenTime(performance.now() - start); + useEffect(() => { + console.log("App has started 🟢"); + const work = async () => { + // let start = performance.now(); + // open({ + // name: 'dummyDb.sqlite', + // }); + // setOpenTime(performance.now() - start); - try { - console.log("TESTS STARTED 🟠"); - const results = await runTests(); - const passed = allTestsPassed(results); - console.log("TESTS FINISHED 🟢") - console.log(`OPSQLITE_TEST_RESULT:${passed ? 'PASS' : 'FAIL'}`); - setResults(results); - } catch (e) { - console.log(`TEST FAILED 🟥 ${e}`) - console.log('OPSQLITE_TEST_RESULT:FAIL'); - } + try { + console.log("TESTS STARTED 🟠"); + const results = await runTests(); + const passed = allTestsPassed(results); + console.log("TESTS FINISHED 🟢"); + console.log(`OPSQLITE_TEST_RESULT:${passed ? "PASS" : "FAIL"}`); + setResults(results); + } catch (e) { + console.log(`TEST FAILED 🟥 ${e}`); + console.log("OPSQLITE_TEST_RESULT:FAIL"); + } - // setTimeout(() => { - // try { - // global?.gc?.(); - // let perfRes = performanceTest(); - // setPerfResult(perfRes); - // } catch (e) { - // // intentionally left blank - // } - // }, 1000); - }; + // setTimeout(() => { + // try { + // global?.gc?.(); + // let perfRes = performanceTest(); + // setPerfResult(perfRes); + // } catch (e) { + // // intentionally left blank + // } + // }, 1000); + }; - work(); + work(); - return () => {}; - }, []); + return () => {}; + }, []); - // const shareDb = async () => { - // try { - // const db = open({ - // name: 'shareableDb.sqlite', - // }); - // await db.execute( - // 'CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT)', - // ); - // await db.execute("INSERT INTO test (name) VALUES ('test')"); - // const res = await db.execute('SELECT * FROM test'); - // console.log(res); - // await db.close(); - // await Share.open({ - // url: 'file://' + db.getDbPath(), - // failOnCancel: false, - // type: 'application/x-sqlite3', - // }); - // } catch (e) { - // console.log(e); - // } - // }; + // const shareDb = async () => { + // try { + // const db = open({ + // name: 'shareableDb.sqlite', + // }); + // await db.execute( + // 'CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT)', + // ); + // await db.execute("INSERT INTO test (name) VALUES ('test')"); + // const res = await db.execute('SELECT * FROM test'); + // console.log(res); + // await db.close(); + // await Share.open({ + // url: 'file://' + db.getDbPath(), + // failOnCancel: false, + // type: 'application/x-sqlite3', + // }); + // } catch (e) { + // console.log(e); + // } + // }; - return ( - - - - - Open DB time: {openTime.toFixed(0)} ms - - - 100_000 query time: {perfResult.toFixed(0)} ms - - - {displayResults(results)} - - - ); + return ( + + + + + Open DB time: {openTime.toFixed(0)} ms + + + 100_000 query time: {perfResult.toFixed(0)} ms + + + {displayResults(results)} + + + ); } const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#222', - gap: 4, - padding: 10, - }, - results: { - flex: 1, - }, - performanceText: { - color: 'white', - fontSize: 16, - }, + container: { + flex: 1, + backgroundColor: "#222", + gap: 4, + padding: 10, + }, + results: { + flex: 1, + }, + performanceText: { + color: "white", + fontSize: 16, + }, }); diff --git a/example/src/AppWeb.tsx b/example/src/AppWeb.tsx index 21d4233a..292111a1 100644 --- a/example/src/AppWeb.tsx +++ b/example/src/AppWeb.tsx @@ -1,377 +1,406 @@ -import { useCallback, useEffect, useState } from 'react'; -import { Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'; -import { openAsync } from '@op-engineering/op-sqlite'; +import { openAsync } from "@op-engineering/op-sqlite"; +import { useCallback, useEffect, useState } from "react"; +import { + Pressable, + ScrollView, + StyleSheet, + Text, + TextInput, + View, +} from "react-native"; type TableColumn = { - cid: number; - name: string; - type: string; - notnull: number; - pk: number; + cid: number; + name: string; + type: string; + notnull: number; + pk: number; }; type Note = { - id: number; - label: string; - created_at: string; + id: number; + label: string; + created_at: string; }; export default function AppWeb() { - const [status, setStatus] = useState('Initializing sqlite web backend...'); - const [columns, setColumns] = useState([]); - const [notes, setNotes] = useState([]); - const [inputValue, setInputValue] = useState('First note from OPFS web db'); - const [loading, setLoading] = useState(false); + const [status, setStatus] = useState("Initializing sqlite web backend..."); + const [columns, setColumns] = useState([]); + const [notes, setNotes] = useState([]); + const [inputValue, setInputValue] = useState("First note from OPFS web db"); + const [loading, setLoading] = useState(false); - const withDb = useCallback( - async (work: (db: Awaited>) => Promise) => { - const db = await openAsync({ - name: 'example-web.sqlite', - }); + const withDb = useCallback( + async ( + work: (db: Awaited>) => Promise, + ) => { + const db = await openAsync({ + name: "example-web.sqlite", + }); - try { - return await work(db); - } finally { - await db.closeAsync(); - } - }, - [] - ); + try { + return await work(db); + } finally { + await db.closeAsync(); + } + }, + [], + ); - const refreshTableInfo = useCallback(async () => { - return withDb(async (db) => { - const [schemaResult, rowsResult] = await Promise.all([ - db.execute('PRAGMA table_info(web_notes)'), - db.execute('SELECT id, label, created_at FROM web_notes ORDER BY id DESC LIMIT 20'), - ]); + const refreshTableInfo = useCallback(async () => { + return withDb(async (db) => { + const [schemaResult, rowsResult] = await Promise.all([ + db.execute("PRAGMA table_info(web_notes)"), + db.execute( + "SELECT id, label, created_at FROM web_notes ORDER BY id DESC LIMIT 20", + ), + ]); - setColumns( - schemaResult.rows.map((row) => ({ - cid: Number(row.cid), - name: String(row.name), - type: String(row.type), - notnull: Number(row.notnull), - pk: Number(row.pk), - })) - ); + setColumns( + schemaResult.rows.map((row) => ({ + cid: Number(row.cid), + name: String(row.name), + type: String(row.type), + notnull: Number(row.notnull), + pk: Number(row.pk), + })), + ); - setNotes( - rowsResult.rows.map((row) => ({ - id: Number(row.id), - label: String(row.label), - created_at: String(row.created_at), - })) - ); - }); - }, [withDb]); + setNotes( + rowsResult.rows.map((row) => ({ + id: Number(row.id), + label: String(row.label), + created_at: String(row.created_at), + })), + ); + }); + }, [withDb]); - const ensureSchema = useCallback(async () => { - return withDb(async (db) => { - await db.execute( - 'CREATE TABLE IF NOT EXISTS web_notes (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT NOT NULL, created_at TEXT NOT NULL)' - ); - }); - }, [withDb]); + const ensureSchema = useCallback(async () => { + return withDb(async (db) => { + await db.execute( + "CREATE TABLE IF NOT EXISTS web_notes (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT NOT NULL, created_at TEXT NOT NULL)", + ); + }); + }, [withDb]); - const insertNote = useCallback(async () => { - const value = inputValue.trim(); - if (!value) { - setStatus('Type a value before inserting.'); - return; - } + const insertNote = useCallback(async () => { + const value = inputValue.trim(); + if (!value) { + setStatus("Type a value before inserting."); + return; + } - setLoading(true); - try { - await withDb(async (db) => { - await db.execute( - 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', - [value, new Date().toISOString()] - ); - }); + setLoading(true); + try { + await withDb(async (db) => { + await db.execute( + "INSERT INTO web_notes (label, created_at) VALUES (?, ?)", + [value, new Date().toISOString()], + ); + }); - setStatus('Insert succeeded. Data is persisted in OPFS.'); - await refreshTableInfo(); - setInputValue(''); - } catch (error) { - setStatus(`Insert failed: ${(error as Error).message}`); - } finally { - setLoading(false); - } - }, [inputValue, refreshTableInfo, withDb]); + setStatus("Insert succeeded. Data is persisted in OPFS."); + await refreshTableInfo(); + setInputValue(""); + } catch (error) { + setStatus(`Insert failed: ${(error as Error).message}`); + } finally { + setLoading(false); + } + }, [inputValue, refreshTableInfo, withDb]); - const insertSampleData = useCallback(async () => { - setLoading(true); - try { - await withDb(async (db) => { - const now = new Date().toISOString(); + const insertSampleData = useCallback(async () => { + setLoading(true); + try { + await withDb(async (db) => { + const now = new Date().toISOString(); - await db.execute( - 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', - ['Sample: OPFS persistence check', now] - ); - await db.execute( - 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', - ['Sample: Query path works', now] - ); - await db.execute( - 'INSERT INTO web_notes (label, created_at) VALUES (?, ?)', - ['Sample: closeAsync completed', now] - ); - }); + await db.execute( + "INSERT INTO web_notes (label, created_at) VALUES (?, ?)", + ["Sample: OPFS persistence check", now], + ); + await db.execute( + "INSERT INTO web_notes (label, created_at) VALUES (?, ?)", + ["Sample: Query path works", now], + ); + await db.execute( + "INSERT INTO web_notes (label, created_at) VALUES (?, ?)", + ["Sample: closeAsync completed", now], + ); + }); - setStatus('Sample rows inserted successfully.'); - await refreshTableInfo(); - } catch (error) { - setStatus(`Sample insert failed: ${(error as Error).message}`); - } finally { - setLoading(false); - } - }, [refreshTableInfo, withDb]); + setStatus("Sample rows inserted successfully."); + await refreshTableInfo(); + } catch (error) { + setStatus(`Sample insert failed: ${(error as Error).message}`); + } finally { + setLoading(false); + } + }, [refreshTableInfo, withDb]); - const clearRows = useCallback(async () => { - setLoading(true); - try { - await withDb(async (db) => { - await db.execute('DELETE FROM web_notes'); - }); + const clearRows = useCallback(async () => { + setLoading(true); + try { + await withDb(async (db) => { + await db.execute("DELETE FROM web_notes"); + }); - setStatus('Table cleared.'); - await refreshTableInfo(); - } catch (error) { - setStatus(`Clear failed: ${(error as Error).message}`); - } finally { - setLoading(false); - } - }, [refreshTableInfo, withDb]); + setStatus("Table cleared."); + await refreshTableInfo(); + } catch (error) { + setStatus(`Clear failed: ${(error as Error).message}`); + } finally { + setLoading(false); + } + }, [refreshTableInfo, withDb]); - useEffect(() => { - const work = async () => { - try { - await ensureSchema(); - await refreshTableInfo(); - setStatus('SQLite web backend initialized with OPFS persistence.'); - } catch (error) { - setStatus( - `Failed to initialize web sqlite backend: ${(error as Error).message}` - ); - } - }; + useEffect(() => { + const work = async () => { + try { + await ensureSchema(); + await refreshTableInfo(); + setStatus("SQLite web backend initialized with OPFS persistence."); + } catch (error) { + setStatus( + `Failed to initialize web sqlite backend: ${(error as Error).message}`, + ); + } + }; - work(); - }, [ensureSchema, refreshTableInfo]); + work(); + }, [ensureSchema, refreshTableInfo]); - return ( - - OP-SQLite Web Example - {status} + return ( + + OP-SQLite Web Example + {status} - - 1) Insert data - - - - Insert one row - - - Insert sample rows - - - + + 1) Insert data + + + + Insert one row + + + Insert sample rows + + + - - Table: web_notes - - - Refresh table - - - Clear table - - + + Table: web_notes + + + Refresh table + + + Clear table + + - Schema - - cid - name - type - nn - pk - - {columns.map((column) => ( - - {column.cid} - {column.name} - {column.type} - {column.notnull} - {column.pk} - - ))} + Schema + + cid + name + type + nn + pk + + {columns.map((column) => ( + + {column.cid} + {column.name} + {column.type} + + {column.notnull} + + {column.pk} + + ))} - Rows ({notes.length}) - - id - label - created_at - - {notes.length === 0 ? ( - No rows in web_notes. - ) : ( - notes.map((entry) => ( - - {entry.id} - {entry.label} - {entry.created_at} - - )) - )} - - - ); + Rows ({notes.length}) + + id + label + created_at + + {notes.length === 0 ? ( + No rows in web_notes. + ) : ( + notes.map((entry) => ( + + {entry.id} + + {entry.label} + + + {entry.created_at} + + + )) + )} + + + ); } const styles = StyleSheet.create({ - container: { - minHeight: '100%', - padding: 24, - backgroundColor: '#101418', - gap: 12, - }, - title: { - color: '#f9fafb', - fontWeight: '700', - fontSize: 24, - marginBottom: 2, - }, - status: { - color: '#9ca3af', - marginBottom: 8, - }, - panel: { - borderWidth: 1, - borderColor: '#273043', - borderRadius: 12, - backgroundColor: '#151a22', - padding: 12, - gap: 8, - }, - panelTitle: { - color: '#f3f4f6', - fontWeight: '600', - fontSize: 16, - }, - sectionLabel: { - color: '#cbd5e1', - marginTop: 4, - marginBottom: 2, - fontWeight: '600', - }, - row: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 8, - }, - input: { - borderWidth: 1, - borderColor: '#334155', - borderRadius: 10, - color: '#f8fafc', - paddingHorizontal: 10, - paddingVertical: 9, - backgroundColor: '#0f172a', - }, - button: { - backgroundColor: '#2563eb', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 9, - }, - buttonSecondary: { - backgroundColor: '#0ea5e9', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 9, - }, - buttonDanger: { - backgroundColor: '#dc2626', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 9, - }, - buttonText: { - color: '#f8fafc', - fontWeight: '600', - }, - empty: { - color: '#9ca3af', - fontStyle: 'italic', - }, - tableHeader: { - flexDirection: 'row', - borderBottomWidth: 1, - borderBottomColor: '#334155', - paddingBottom: 6, - marginTop: 4, - }, - tableRow: { - flexDirection: 'row', - borderBottomWidth: 1, - borderBottomColor: '#1f2937', - paddingVertical: 8, - }, - headerCell: { - color: '#93c5fd', - fontWeight: '700', - fontSize: 12, - }, - rowCell: { - color: '#e5e7eb', - fontSize: 13, - }, - cidCell: { - width: 40, - }, - nameCell: { - flex: 1, - paddingRight: 8, - }, - typeCell: { - width: 80, - }, - flagCell: { - width: 32, - }, - dateCell: { - flex: 1, - paddingLeft: 8, - }, - item: { - borderWidth: 1, - borderColor: '#334155', - borderRadius: 10, - padding: 9, - gap: 2, - backgroundColor: '#0f172a', - }, - itemTitle: { - color: '#93c5fd', - fontWeight: '700', - }, - itemText: { - color: '#f1f5f9', - }, - itemMeta: { - color: '#94a3b8', - fontSize: 14, - }, + container: { + minHeight: "100%", + padding: 24, + backgroundColor: "#101418", + gap: 12, + }, + title: { + color: "#f9fafb", + fontWeight: "700", + fontSize: 24, + marginBottom: 2, + }, + status: { + color: "#9ca3af", + marginBottom: 8, + }, + panel: { + borderWidth: 1, + borderColor: "#273043", + borderRadius: 12, + backgroundColor: "#151a22", + padding: 12, + gap: 8, + }, + panelTitle: { + color: "#f3f4f6", + fontWeight: "600", + fontSize: 16, + }, + sectionLabel: { + color: "#cbd5e1", + marginTop: 4, + marginBottom: 2, + fontWeight: "600", + }, + row: { + flexDirection: "row", + flexWrap: "wrap", + gap: 8, + }, + input: { + borderWidth: 1, + borderColor: "#334155", + borderRadius: 10, + color: "#f8fafc", + paddingHorizontal: 10, + paddingVertical: 9, + backgroundColor: "#0f172a", + }, + button: { + backgroundColor: "#2563eb", + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 9, + }, + buttonSecondary: { + backgroundColor: "#0ea5e9", + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 9, + }, + buttonDanger: { + backgroundColor: "#dc2626", + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 9, + }, + buttonText: { + color: "#f8fafc", + fontWeight: "600", + }, + empty: { + color: "#9ca3af", + fontStyle: "italic", + }, + tableHeader: { + flexDirection: "row", + borderBottomWidth: 1, + borderBottomColor: "#334155", + paddingBottom: 6, + marginTop: 4, + }, + tableRow: { + flexDirection: "row", + borderBottomWidth: 1, + borderBottomColor: "#1f2937", + paddingVertical: 8, + }, + headerCell: { + color: "#93c5fd", + fontWeight: "700", + fontSize: 12, + }, + rowCell: { + color: "#e5e7eb", + fontSize: 13, + }, + cidCell: { + width: 40, + }, + nameCell: { + flex: 1, + paddingRight: 8, + }, + typeCell: { + width: 80, + }, + flagCell: { + width: 32, + }, + dateCell: { + flex: 1, + paddingLeft: 8, + }, + item: { + borderWidth: 1, + borderColor: "#334155", + borderRadius: 10, + padding: 9, + gap: 2, + backgroundColor: "#0f172a", + }, + itemTitle: { + color: "#93c5fd", + fontWeight: "700", + }, + itemText: { + color: "#f1f5f9", + }, + itemMeta: { + color: "#94a3b8", + fontSize: 14, + }, }); diff --git a/example/src/tests/blob.ts b/example/src/tests/blob.ts index b1d8ca30..e3c018bc 100644 --- a/example/src/tests/blob.ts +++ b/example/src/tests/blob.ts @@ -1,112 +1,112 @@ -import {type DB, open} from '@op-engineering/op-sqlite'; +import { type DB, open } from "@op-engineering/op-sqlite"; import { - afterAll, - beforeEach, - describe, - it, - expect, -} from '@op-engineering/op-test'; + afterAll, + beforeEach, + describe, + expect, + it, +} from "@op-engineering/op-test"; let db: DB; -describe('Blobs', () => { - beforeEach(async () => { - try { - db = open({ - name: 'blobs', - encryptionKey: 'test', - }); +describe("Blobs", () => { + beforeEach(async () => { + try { + db = open({ + name: "blobs", + encryptionKey: "test", + }); - await db.execute('DROP TABLE IF EXISTS BlobTable;'); - await db.execute( - 'CREATE TABLE BlobTable ( id INT PRIMARY KEY, content BLOB) STRICT;', - ); - } catch (e) { - console.warn('error on before each', e); - } - }); + await db.execute("DROP TABLE IF EXISTS BlobTable;"); + await db.execute( + "CREATE TABLE BlobTable ( id INT PRIMARY KEY, content BLOB) STRICT;", + ); + } catch (e) { + console.warn("error on before each", e); + } + }); - afterAll(() => { - db.delete(); - }); + afterAll(() => { + db.delete(); + }); - it('ArrayBuffer', async () => { - const uint8 = new Uint8Array(2); - uint8[0] = 42; + it("ArrayBuffer", async () => { + const uint8 = new Uint8Array(2); + uint8[0] = 42; - await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [ - 1, - uint8.buffer, - ]); + await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [ + 1, + uint8.buffer, + ]); - const result = await db.execute('SELECT content FROM BlobTable;'); + const result = await db.execute("SELECT content FROM BlobTable;"); - const finalUint8 = new Uint8Array(result.rows[0]!.content as any); - expect(finalUint8[0]).toBe(42); - }); + const finalUint8 = new Uint8Array(result.rows[0]!.content as any); + expect(finalUint8[0]).toBe(42); + }); - it('Uint8Array', async () => { - const uint8 = new Uint8Array(2); - uint8[0] = 42; + it("Uint8Array", async () => { + const uint8 = new Uint8Array(2); + uint8[0] = 42; - await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [ - 1, - uint8, - ]); + await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [ + 1, + uint8, + ]); - const result = await db.execute('SELECT content FROM BlobTable'); + const result = await db.execute("SELECT content FROM BlobTable"); - const finalUint8 = new Uint8Array(result.rows[0]!.content as any); - expect(finalUint8[0]).toBe(42); - }); + const finalUint8 = new Uint8Array(result.rows[0]!.content as any); + expect(finalUint8[0]).toBe(42); + }); - it('Uint16Array', async () => { - const uint8 = new Uint16Array(2); - uint8[0] = 42; + it("Uint16Array", async () => { + const uint8 = new Uint16Array(2); + uint8[0] = 42; - await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [ - 1, - uint8, - ]); + await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [ + 1, + uint8, + ]); - const result = await db.execute('SELECT content FROM BlobTable'); + const result = await db.execute("SELECT content FROM BlobTable"); - const finalUint8 = new Uint8Array(result.rows[0]!.content as any); - expect(finalUint8[0]).toBe(42); - }); + const finalUint8 = new Uint8Array(result.rows[0]!.content as any); + expect(finalUint8[0]).toBe(42); + }); - it('Uint8Array in prepared statement', async () => { - const uint8 = new Uint8Array(2); - uint8[0] = 46; + it("Uint8Array in prepared statement", async () => { + const uint8 = new Uint8Array(2); + uint8[0] = 46; - const statement = db.prepareStatement( - 'INSERT OR REPLACE INTO BlobTable VALUES (?, ?);', - ); - await statement.bind([1, uint8]); + const statement = db.prepareStatement( + "INSERT OR REPLACE INTO BlobTable VALUES (?, ?);", + ); + await statement.bind([1, uint8]); - await statement.execute(); + await statement.execute(); - const result = await db.execute('SELECT content FROM BlobTable'); + const result = await db.execute("SELECT content FROM BlobTable"); - const finalUint8 = new Uint8Array(result.rows[0]!.content as any); - expect(finalUint8[0]).toBe(46); - }); + const finalUint8 = new Uint8Array(result.rows[0]!.content as any); + expect(finalUint8[0]).toBe(46); + }); - it('Buffer in prepared statement', async () => { - const uint8 = new Uint8Array(2); - uint8[0] = 52; + it("Buffer in prepared statement", async () => { + const uint8 = new Uint8Array(2); + uint8[0] = 52; - const statement = db.prepareStatement( - 'INSERT OR REPLACE INTO BlobTable VALUES (?, ?);', - ); + const statement = db.prepareStatement( + "INSERT OR REPLACE INTO BlobTable VALUES (?, ?);", + ); - await statement.bind([1, uint8.buffer]); + await statement.bind([1, uint8.buffer]); - await statement.execute(); + await statement.execute(); - const result = await db.execute('SELECT content FROM BlobTable'); + const result = await db.execute("SELECT content FROM BlobTable"); - const finalUint8 = new Uint8Array(result.rows[0]!.content as any); - expect(finalUint8[0]).toBe(52); - }); + const finalUint8 = new Uint8Array(result.rows[0]!.content as any); + expect(finalUint8[0]).toBe(52); + }); }); diff --git a/example/src/tests/constants.ts b/example/src/tests/constants.ts index 3ebdf9bc..45376dae 100644 --- a/example/src/tests/constants.ts +++ b/example/src/tests/constants.ts @@ -1,22 +1,22 @@ import { - IOS_DOCUMENT_PATH, - IOS_LIBRARY_PATH, - ANDROID_EXTERNAL_FILES_PATH, - ANDROID_DATABASE_PATH, - ANDROID_FILES_PATH, -} from '@op-engineering/op-sqlite'; -import {describe, it, expect} from '@op-engineering/op-test'; -import {Platform} from 'react-native'; + ANDROID_DATABASE_PATH, + ANDROID_EXTERNAL_FILES_PATH, + ANDROID_FILES_PATH, + IOS_DOCUMENT_PATH, + IOS_LIBRARY_PATH, +} from "@op-engineering/op-sqlite"; +import { describe, expect, it } from "@op-engineering/op-test"; +import { Platform } from "react-native"; -describe('Constants tests', () => { - it('Constants are there', async () => { - if (Platform.OS === 'ios') { - expect(!!IOS_DOCUMENT_PATH).toBeTruthy(); - expect(!!IOS_LIBRARY_PATH).toBeTruthy(); - } else { - expect(!!ANDROID_EXTERNAL_FILES_PATH).toBeTruthy(); - expect(!!ANDROID_DATABASE_PATH).toBeTruthy(); - expect(!!ANDROID_FILES_PATH).toBeTruthy(); - } - }); +describe("Constants tests", () => { + it("Constants are there", async () => { + if (Platform.OS === "ios") { + expect(!!IOS_DOCUMENT_PATH).toBeTruthy(); + expect(!!IOS_LIBRARY_PATH).toBeTruthy(); + } else { + expect(!!ANDROID_EXTERNAL_FILES_PATH).toBeTruthy(); + expect(!!ANDROID_DATABASE_PATH).toBeTruthy(); + expect(!!ANDROID_FILES_PATH).toBeTruthy(); + } + }); }); diff --git a/example/src/tests/dbsetup.ts b/example/src/tests/dbsetup.ts index c1a2457f..bf922a4a 100644 --- a/example/src/tests/dbsetup.ts +++ b/example/src/tests/dbsetup.ts @@ -1,290 +1,290 @@ import { - ANDROID_DATABASE_PATH, - ANDROID_EXTERNAL_FILES_PATH, - IOS_LIBRARY_PATH, - isIOSEmbeeded, - isLibsql, - isSQLCipher, - moveAssetsDatabase, - open, -} from '@op-engineering/op-sqlite'; -import {describe, it, expect} from '@op-engineering/op-test'; -import {Platform} from 'react-native'; + ANDROID_DATABASE_PATH, + ANDROID_EXTERNAL_FILES_PATH, + IOS_LIBRARY_PATH, + isIOSEmbeeded, + isLibsql, + isSQLCipher, + moveAssetsDatabase, + open, +} from "@op-engineering/op-sqlite"; +import { describe, expect, it } from "@op-engineering/op-test"; +import { Platform } from "react-native"; const expectedVersion = isLibsql() - ? '3.45.1' - : isSQLCipher() - ? '3.51.3' - : '3.51.3'; -const flavor = isLibsql() ? 'libsql' : isSQLCipher() ? 'sqlcipher' : 'sqlite'; + ? "3.45.1" + : isSQLCipher() + ? "3.51.3" + : "3.51.3"; +const flavor = isLibsql() ? "libsql" : isSQLCipher() ? "sqlcipher" : "sqlite"; // const expectedSqliteVecVersion = 'v0.1.2-alpha.7'; -describe('DB setup tests', () => { - // it('Should match the sqlite_vec version', async () => { - // let db = open({ - // name: 'versionTest.sqlite', - // }); - - // const res = db.execute('select vec_version();'); - - // expect(res.rows?._array[0]['vec_version()']).to.equal( - // expectedSqliteVecVersion, - // ); - - // db.close(); - // }); - - // Using the embedded version, you can never be sure which version is used - // It will change from OS version to version - if (!isIOSEmbeeded()) { - it(`Should match the sqlite flavor ${flavor} expected version ${expectedVersion}`, async () => { - let db = open({ - name: 'versionTest.sqlite', - encryptionKey: 'test', - }); - - const res = await db.execute('select sqlite_version();'); - - expect(res.rows[0]!['sqlite_version()']).toBe(expectedVersion); - db.close(); - }); - } - - it('Create in memory DB', async () => { - let inMemoryDb = open({ - name: 'inMemoryTest.sqlite', - location: ':memory:', - encryptionKey: 'test', - }); - - await inMemoryDb.execute('DROP TABLE IF EXISTS User;'); - await inMemoryDb.execute( - 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;', - ); - - inMemoryDb.close(); - }); - - if (Platform.OS === 'android') { - it('Create db in external directory Android', async () => { - let androidDb = open({ - name: 'AndroidSDCardDB.sqlite', - location: ANDROID_EXTERNAL_FILES_PATH, - encryptionKey: 'test', - }); - - await androidDb.execute('DROP TABLE IF EXISTS User;'); - await androidDb.execute( - 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;', - ); - - androidDb.close(); - }); - - it('Creates db in external nested directory on Android', async () => { - let androidDb = open({ - name: 'AndroidSDCardDB.sqlite', - location: `${ANDROID_EXTERNAL_FILES_PATH}/nested`, - encryptionKey: 'test', - }); - - await androidDb.execute('DROP TABLE IF EXISTS User;'); - await androidDb.execute( - 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;', - ); - - androidDb.close(); - }); - } - - // Currently this only tests the function is there - it('Should load extension', async () => { - let db = open({ - name: 'extensionDb', - encryptionKey: 'test', - }); - - try { - db.loadExtension('path'); - } catch (e) { - // TODO load a sample extension - expect(!!e).toEqual(true); - } finally { - db.delete(); - } - }); - - it('Should delete db', async () => { - let db = open({ - name: 'deleteTest', - encryptionKey: 'test', - }); - - db.delete(); - }); - - it('Should delete db with absolute path', async () => { - let location = - Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH; - let db = open({ - name: 'deleteTest', - encryptionKey: 'test', - location, - }); - - expect(db.getDbPath().includes(location)).toEqual(true); - - db.delete(); - }); - - it('Should create db in custom folder', async () => { - let db = open({ - name: 'customFolderTest.sqlite', - encryptionKey: 'test', - location: 'myFolder', - }); - - let path = db.getDbPath(); - expect(path.includes('myFolder')).toEqual(true); - db.delete(); - }); - - it('Should create nested folders', async () => { - let db = open({ - name: 'nestedFolderTest.sqlite', - encryptionKey: 'test', - location: 'myFolder/nested', - }); - - let path = db.getDbPath(); - expect(path.includes('myFolder/nested')).toEqual(true); - db.delete(); - }); - - it('Moves assets database simple', async () => { - const copied = await moveAssetsDatabase({filename: 'sample.sqlite'}); - - expect(copied).toEqual(true); - }); - - it('Moves assets database with path', async () => { - const copied = await moveAssetsDatabase({ - filename: 'sample2.sqlite', - path: 'sqlite', - }); - - expect(copied).toEqual(true); - }); - - it('Moves assets database with path and overwrite', async () => { - const copied = await moveAssetsDatabase({ - filename: 'sample2.sqlite', - path: 'sqlite', - overwrite: true, - }); - - expect(copied).toEqual(true); - - let db = open({ - name: 'sample2.sqlite', - encryptionKey: 'test', - location: 'sqlite', - }); - - let path = db.getDbPath(); - expect(path.includes('sqlite/sample2.sqlite')).toEqual(true); - db.delete(); - }); - - it('Creates new connections per query and closes them', async () => { - for (let i = 0; i < 100; i++) { - let db = open({ - name: 'versionTest.sqlite', - encryptionKey: 'test', - }); - - await db.execute('select 1;'); - - db.close(); - } - }); - - it('Closes connections correctly', async () => { - try { - let db1 = open({ - name: 'closeTest.sqlite', - }); - expect(!!db1).toBe(true); - open({ - name: 'closeTest.sqlite', - }); - } catch (e) { - expect(!!e).toBe(true); - } - }); +describe("DB setup tests", () => { + // it('Should match the sqlite_vec version', async () => { + // let db = open({ + // name: 'versionTest.sqlite', + // }); + + // const res = db.execute('select vec_version();'); + + // expect(res.rows?._array[0]['vec_version()']).to.equal( + // expectedSqliteVecVersion, + // ); + + // db.close(); + // }); + + // Using the embedded version, you can never be sure which version is used + // It will change from OS version to version + if (!isIOSEmbeeded()) { + it(`Should match the sqlite flavor ${flavor} expected version ${expectedVersion}`, async () => { + const db = open({ + name: "versionTest.sqlite", + encryptionKey: "test", + }); + + const res = await db.execute("select sqlite_version();"); + + expect(res.rows[0]!["sqlite_version()"]).toBe(expectedVersion); + db.close(); + }); + } + + it("Create in memory DB", async () => { + const inMemoryDb = open({ + name: "inMemoryTest.sqlite", + location: ":memory:", + encryptionKey: "test", + }); + + await inMemoryDb.execute("DROP TABLE IF EXISTS User;"); + await inMemoryDb.execute( + "CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;", + ); + + inMemoryDb.close(); + }); + + if (Platform.OS === "android") { + it("Create db in external directory Android", async () => { + const androidDb = open({ + name: "AndroidSDCardDB.sqlite", + location: ANDROID_EXTERNAL_FILES_PATH, + encryptionKey: "test", + }); + + await androidDb.execute("DROP TABLE IF EXISTS User;"); + await androidDb.execute( + "CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;", + ); + + androidDb.close(); + }); + + it("Creates db in external nested directory on Android", async () => { + const androidDb = open({ + name: "AndroidSDCardDB.sqlite", + location: `${ANDROID_EXTERNAL_FILES_PATH}/nested`, + encryptionKey: "test", + }); + + await androidDb.execute("DROP TABLE IF EXISTS User;"); + await androidDb.execute( + "CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;", + ); + + androidDb.close(); + }); + } + + // Currently this only tests the function is there + it("Should load extension", async () => { + const db = open({ + name: "extensionDb", + encryptionKey: "test", + }); + + try { + db.loadExtension("path"); + } catch (e) { + // TODO load a sample extension + expect(!!e).toEqual(true); + } finally { + db.delete(); + } + }); + + it("Should delete db", async () => { + const db = open({ + name: "deleteTest", + encryptionKey: "test", + }); + + db.delete(); + }); + + it("Should delete db with absolute path", async () => { + const location = + Platform.OS === "ios" ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH; + const db = open({ + name: "deleteTest", + encryptionKey: "test", + location, + }); + + expect(db.getDbPath().includes(location)).toEqual(true); + + db.delete(); + }); + + it("Should create db in custom folder", async () => { + const db = open({ + name: "customFolderTest.sqlite", + encryptionKey: "test", + location: "myFolder", + }); + + const path = db.getDbPath(); + expect(path.includes("myFolder")).toEqual(true); + db.delete(); + }); + + it("Should create nested folders", async () => { + const db = open({ + name: "nestedFolderTest.sqlite", + encryptionKey: "test", + location: "myFolder/nested", + }); + + const path = db.getDbPath(); + expect(path.includes("myFolder/nested")).toEqual(true); + db.delete(); + }); + + it("Moves assets database simple", async () => { + const copied = await moveAssetsDatabase({ filename: "sample.sqlite" }); + + expect(copied).toEqual(true); + }); + + it("Moves assets database with path", async () => { + const copied = await moveAssetsDatabase({ + filename: "sample2.sqlite", + path: "sqlite", + }); + + expect(copied).toEqual(true); + }); + + it("Moves assets database with path and overwrite", async () => { + const copied = await moveAssetsDatabase({ + filename: "sample2.sqlite", + path: "sqlite", + overwrite: true, + }); + + expect(copied).toEqual(true); + + const db = open({ + name: "sample2.sqlite", + encryptionKey: "test", + location: "sqlite", + }); + + const path = db.getDbPath(); + expect(path.includes("sqlite/sample2.sqlite")).toEqual(true); + db.delete(); + }); + + it("Creates new connections per query and closes them", async () => { + for (let i = 0; i < 100; i++) { + const db = open({ + name: "versionTest.sqlite", + encryptionKey: "test", + }); + + await db.execute("select 1;"); + + db.close(); + } + }); + + it("Closes connections correctly", async () => { + try { + const db1 = open({ + name: "closeTest.sqlite", + }); + expect(!!db1).toBe(true); + open({ + name: "closeTest.sqlite", + }); + } catch (e) { + expect(!!e).toBe(true); + } + }); }); -it('Can attach/dettach database', () => { - let db = open({ - name: 'attachTest.sqlite', - encryptionKey: 'test', - }); - let db2 = open({ - name: 'attachTest2.sqlite', - encryptionKey: 'test', - }); - db2.close(); - - db.attach({ - secondaryDbFileName: 'attachTest2.sqlite', - alias: 'attach2', - }); - - db.executeSync('DROP TABLE IF EXISTS attach2.test;'); - db.executeSync( - 'CREATE TABLE IF NOT EXISTS attach2.test (id INTEGER PRIMARY KEY);', - ); - let res = db.executeSync('INSERT INTO attach2.test (id) VALUES (1);'); - expect(!!res).toBe(true); - - db.detach('attach2'); - - db.delete(); - - db2 = open({ - name: 'attachTest2.sqlite', - encryptionKey: 'test', - }); - db2.delete(); +it("Can attach/dettach database", () => { + const db = open({ + name: "attachTest.sqlite", + encryptionKey: "test", + }); + let db2 = open({ + name: "attachTest2.sqlite", + encryptionKey: "test", + }); + db2.close(); + + db.attach({ + secondaryDbFileName: "attachTest2.sqlite", + alias: "attach2", + }); + + db.executeSync("DROP TABLE IF EXISTS attach2.test;"); + db.executeSync( + "CREATE TABLE IF NOT EXISTS attach2.test (id INTEGER PRIMARY KEY);", + ); + const res = db.executeSync("INSERT INTO attach2.test (id) VALUES (1);"); + expect(!!res).toBe(true); + + db.detach("attach2"); + + db.delete(); + + db2 = open({ + name: "attachTest2.sqlite", + encryptionKey: "test", + }); + db2.delete(); }); -it('Can get db path', () => { - let db = open({ - name: 'pathTest.sqlite', - encryptionKey: 'test', - }); +it("Can get db path", () => { + const db = open({ + name: "pathTest.sqlite", + encryptionKey: "test", + }); - let path = db.getDbPath(); - expect(!!path).toBe(true); - db.close(); + const path = db.getDbPath(); + expect(!!path).toBe(true); + db.close(); }); if (isLibsql()) { - it('Libsql can set reserved bytes', async () => { - const db = open({name: 'test.db'}); - db.setReservedBytes(28); - expect(db.getReservedBytes()).toEqual(28); - db.delete(); - }); + it("Libsql can set reserved bytes", async () => { + const db = open({ name: "test.db" }); + db.setReservedBytes(28); + expect(db.getReservedBytes()).toEqual(28); + db.delete(); + }); } if (isSQLCipher()) { - it('Can open SQLCipher db without encryption key', () => { - let db = open({ - name: 'pathTest.sqlite', - }); + it("Can open SQLCipher db without encryption key", () => { + const db = open({ + name: "pathTest.sqlite", + }); - db.close(); - }); + db.close(); + }); } diff --git a/example/src/tests/hooks.ts b/example/src/tests/hooks.ts index 1397b636..daa38e7d 100644 --- a/example/src/tests/hooks.ts +++ b/example/src/tests/hooks.ts @@ -1,250 +1,249 @@ -import Chance from 'chance'; - -import {type DB, open, isLibsql} from '@op-engineering/op-sqlite'; +import { type DB, isLibsql, open } from "@op-engineering/op-sqlite"; import { - expect, - describe, - it, - beforeEach, - afterEach, -} from '@op-engineering/op-test'; -import {sleep} from './utils'; + afterEach, + beforeEach, + describe, + expect, + it, +} from "@op-engineering/op-test"; +import Chance from "chance"; +import { sleep } from "./utils"; const DB_CONFIG = { - name: 'hooksDb', - encryptionKey: 'test', + name: "hooksDb", + encryptionKey: "test", }; const chance = new Chance(); -describe('Hooks', () => { - let db: DB; - if (isLibsql()) { - return; - } - - beforeEach(async () => { - try { - db = open(DB_CONFIG); - - await db.execute('DROP TABLE IF EXISTS User;'); - await db.execute( - 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;', - ); - } catch (e) { - console.warn('error on before each', e); - } - }); - - afterEach(() => { - if (db) { - db.delete(); - } - }); - - it('update hook', async () => { - let promiseResolve: any; - let promise = new Promise<{ - rowId: number; - row?: any; - operation: string; - table: string; - }>(resolve => { - promiseResolve = resolve; - }); - - db.updateHook(data => { - promiseResolve(data); - }); - - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - }); - - const data = await promise; - - expect(data.operation).toEqual('INSERT'); - expect(data.rowId).toEqual(1); - - db.updateHook(null); - }); - - it('Execute batch should trigger update hook', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - db.executeSync( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - let promiseResolve: any; - let promise = new Promise<{ - rowId: number; - row?: any; - operation: string; - table: string; - }>(resolve => { - promiseResolve = resolve; - }); - - db.updateHook(data => { - promiseResolve(data); - }); - - await db.executeBatch([ - ['UPDATE "User" SET name = ? WHERE id = ?', ['foo', id]], - ]); - - const data = await promise; - - expect(data.operation).toEqual('UPDATE'); - expect(data.rowId).toEqual(1); - }); - - it('remove update hook', async () => { - const hookRes: string[] = []; - - db.updateHook(({operation}) => { - hookRes.push(operation); - }); - - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - }); - - db.updateHook(null); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id + 1, name, age, networth], - ); - }); - - await sleep(0); - - expect(hookRes.length).toEqual(1); - }); - - it('commit hook', async () => { - let promiseResolve: any; - let promise = new Promise(resolve => { - promiseResolve = resolve; - }); - - db.commitHook(() => { - promiseResolve?.(); - }); - - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - }); - - await promise; - db.commitHook(null); - }); - - it('remove commit hook', async () => { - const hookRes: string[] = []; - db.commitHook(() => { - hookRes.push('commit'); - }); - - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - }); - - db.commitHook(null); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id + 1, name, age, networth], - ); - }); - - await sleep(0); - - expect(hookRes.length).toEqual(1); - }); - - it('rollback hook', async () => { - let promiseResolve: any; - let promise = new Promise(resolve => { - promiseResolve = resolve; - }); - - db.rollbackHook(() => { - promiseResolve?.(); - }); - - try { - await db.transaction(async () => { - throw new Error('Blah'); - }); - } catch (e) { - // intentionally left blank - } - - await promise; - }); - - it('remove rollback hook', async () => { - const hookRes: string[] = []; - db.rollbackHook(() => { - hookRes.push('rollback'); - }); - - try { - await db.transaction(async () => { - throw new Error('Blah'); - }); - } catch (e) { - // intentionally left blank - } - - db.rollbackHook(null); - - try { - await db.transaction(async () => { - throw new Error('Blah'); - }); - } catch (e) { - // intentionally left blank - } - - await sleep(0); - - expect(hookRes.length).toEqual(1); - }); +describe("Hooks", () => { + let db: DB; + if (isLibsql()) { + return; + } + + beforeEach(async () => { + try { + db = open(DB_CONFIG); + + await db.execute("DROP TABLE IF EXISTS User;"); + await db.execute( + "CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;", + ); + } catch (e) { + console.warn("error on before each", e); + } + }); + + afterEach(() => { + if (db) { + db.delete(); + } + }); + + it("update hook", async () => { + let promiseResolve: any; + const promise = new Promise<{ + rowId: number; + row?: any; + operation: string; + table: string; + }>((resolve) => { + promiseResolve = resolve; + }); + + db.updateHook((data) => { + promiseResolve(data); + }); + + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + }); + + const data = await promise; + + expect(data.operation).toEqual("INSERT"); + expect(data.rowId).toEqual(1); + + db.updateHook(null); + }); + + it("Execute batch should trigger update hook", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + db.executeSync( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + let promiseResolve: any; + const promise = new Promise<{ + rowId: number; + row?: any; + operation: string; + table: string; + }>((resolve) => { + promiseResolve = resolve; + }); + + db.updateHook((data) => { + promiseResolve(data); + }); + + await db.executeBatch([ + ['UPDATE "User" SET name = ? WHERE id = ?', ["foo", id]], + ]); + + const data = await promise; + + expect(data.operation).toEqual("UPDATE"); + expect(data.rowId).toEqual(1); + }); + + it("remove update hook", async () => { + const hookRes: string[] = []; + + db.updateHook(({ operation }) => { + hookRes.push(operation); + }); + + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + }); + + db.updateHook(null); + + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id + 1, name, age, networth], + ); + }); + + await sleep(0); + + expect(hookRes.length).toEqual(1); + }); + + it("commit hook", async () => { + let promiseResolve: any; + const promise = new Promise((resolve) => { + promiseResolve = resolve; + }); + + db.commitHook(() => { + promiseResolve?.(); + }); + + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + }); + + await promise; + db.commitHook(null); + }); + + it("remove commit hook", async () => { + const hookRes: string[] = []; + db.commitHook(() => { + hookRes.push("commit"); + }); + + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + }); + + db.commitHook(null); + + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id + 1, name, age, networth], + ); + }); + + await sleep(0); + + expect(hookRes.length).toEqual(1); + }); + + it("rollback hook", async () => { + let promiseResolve: any; + const promise = new Promise((resolve) => { + promiseResolve = resolve; + }); + + db.rollbackHook(() => { + promiseResolve?.(); + }); + + try { + await db.transaction(async () => { + throw new Error("Blah"); + }); + } catch (e) { + // intentionally left blank + } + + await promise; + }); + + it("remove rollback hook", async () => { + const hookRes: string[] = []; + db.rollbackHook(() => { + hookRes.push("rollback"); + }); + + try { + await db.transaction(async () => { + throw new Error("Blah"); + }); + } catch (e) { + // intentionally left blank + } + + db.rollbackHook(null); + + try { + await db.transaction(async () => { + throw new Error("Blah"); + }); + } catch (e) { + // intentionally left blank + } + + await sleep(0); + + expect(hookRes.length).toEqual(1); + }); }); diff --git a/example/src/tests/index.ts b/example/src/tests/index.ts index 3044ff24..3e229734 100644 --- a/example/src/tests/index.ts +++ b/example/src/tests/index.ts @@ -1,10 +1,10 @@ -import './blob'; -import './constants'; -import './dbsetup'; -import './hooks'; -import './preparedStatements'; -import './queries'; -import './reactive'; -import './storage'; -import './tokenizer'; -import './web'; +import "./blob"; +import "./constants"; +import "./dbsetup"; +import "./hooks"; +import "./preparedStatements"; +import "./queries"; +import "./reactive"; +import "./storage"; +import "./tokenizer"; +import "./web"; diff --git a/example/src/tests/preparedStatements.ts b/example/src/tests/preparedStatements.ts index 36251882..f631abb8 100644 --- a/example/src/tests/preparedStatements.ts +++ b/example/src/tests/preparedStatements.ts @@ -1,102 +1,102 @@ -import {open, type DB} from '@op-engineering/op-sqlite'; +import { type DB, open } from "@op-engineering/op-sqlite"; import { - afterAll, - beforeEach, - describe, - it, - expect, -} from '@op-engineering/op-test'; - -describe('PreparedStatements', () => { - let db: DB; - beforeEach(async () => { - try { - if (db) { - db.close(); - db.delete(); - } - - db = open({ - name: 'statements', - encryptionKey: 'test', - }); - - await db.execute('DROP TABLE IF EXISTS User;'); - await db.execute( - 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT) STRICT;', - ); - await db.execute('INSERT INTO "User" (id, name) VALUES(?,?)', [ - 1, - 'Oscar', - ]); - await db.execute('INSERT INTO "User" (id, name) VALUES(?,?)', [ - 2, - 'Pablo', - ]); - await db.execute('INSERT INTO "User" (id, name) VALUES(?,?)', [ - 3, - 'Carlos', - ]); - } catch (e) { - console.warn('error on before each', e); - } - }); - - afterAll(() => { - if (db) { - db.close(); - db.delete(); - // @ts-ignore - db = null; - } - }); - - it('Creates a prepared statement and executes a prepared statement', async () => { - const statement = db.prepareStatement('SELECT * FROM User;'); - let results = await statement.execute(); - - expect(results.rows?.length).toEqual(3); - results = await statement.execute(); - - expect(results.rows.length).toEqual(3); - }); - - it('prepared statement, rebind select', async () => { - const statement = db.prepareStatement('SELECT * FROM User WHERE id = ?;'); - await statement.bind([1]); - - let results = await statement.execute(); - expect(results.rows[0]!.name === 'Oscar'); - - await statement.bind([2]); - results = await statement.execute(); - expect(results.rows[0]!.name === 'Pablo'); - }); - - it('prepared statement, rebind insert', async () => { - const statement = db.prepareStatement( - 'INSERT INTO "User" (id, name) VALUES(?,?);', - ); - await statement.bind([4, 'Juan']); - await statement.execute(); - - await statement.bind([5, 'Pedro']); - await statement.execute(); - }); - - it('prepared statement, bindsync', async () => { - const statement = db.prepareStatement( - 'INSERT INTO "User" (id, name) VALUES(?,?);', - ); - - statement.bindSync([4, 'Juan']); - await statement.execute(); - - statement.bind([5, 'Pedro']); - await statement.execute(); - - const selectStatement = db.prepareStatement('SELECT * FROM User;'); - let results = await selectStatement.execute(); - expect(results.rows.length).toEqual(5); - }); + afterAll, + beforeEach, + describe, + expect, + it, +} from "@op-engineering/op-test"; + +describe("PreparedStatements", () => { + let db: DB; + beforeEach(async () => { + try { + if (db) { + db.close(); + db.delete(); + } + + db = open({ + name: "statements", + encryptionKey: "test", + }); + + await db.execute("DROP TABLE IF EXISTS User;"); + await db.execute( + "CREATE TABLE User ( id INT PRIMARY KEY, name TEXT) STRICT;", + ); + await db.execute('INSERT INTO "User" (id, name) VALUES(?,?)', [ + 1, + "Oscar", + ]); + await db.execute('INSERT INTO "User" (id, name) VALUES(?,?)', [ + 2, + "Pablo", + ]); + await db.execute('INSERT INTO "User" (id, name) VALUES(?,?)', [ + 3, + "Carlos", + ]); + } catch (e) { + console.warn("error on before each", e); + } + }); + + afterAll(() => { + if (db) { + db.close(); + db.delete(); + // @ts-expect-error + db = null; + } + }); + + it("Creates a prepared statement and executes a prepared statement", async () => { + const statement = db.prepareStatement("SELECT * FROM User;"); + let results = await statement.execute(); + + expect(results.rows?.length).toEqual(3); + results = await statement.execute(); + + expect(results.rows.length).toEqual(3); + }); + + it("prepared statement, rebind select", async () => { + const statement = db.prepareStatement("SELECT * FROM User WHERE id = ?;"); + await statement.bind([1]); + + let results = await statement.execute(); + expect(results.rows[0]!.name === "Oscar"); + + await statement.bind([2]); + results = await statement.execute(); + expect(results.rows[0]!.name === "Pablo"); + }); + + it("prepared statement, rebind insert", async () => { + const statement = db.prepareStatement( + 'INSERT INTO "User" (id, name) VALUES(?,?);', + ); + await statement.bind([4, "Juan"]); + await statement.execute(); + + await statement.bind([5, "Pedro"]); + await statement.execute(); + }); + + it("prepared statement, bindsync", async () => { + const statement = db.prepareStatement( + 'INSERT INTO "User" (id, name) VALUES(?,?);', + ); + + statement.bindSync([4, "Juan"]); + await statement.execute(); + + statement.bind([5, "Pedro"]); + await statement.execute(); + + const selectStatement = db.prepareStatement("SELECT * FROM User;"); + const results = await selectStatement.execute(); + expect(results.rows.length).toEqual(5); + }); }); diff --git a/example/src/tests/queries.ts b/example/src/tests/queries.ts index 8fe2f997..e5cbcd47 100644 --- a/example/src/tests/queries.ts +++ b/example/src/tests/queries.ts @@ -1,394 +1,395 @@ -import {chance} from './utils'; import { - isLibsql, - open, - // openRemote, - // openSync, - type DB, - type SQLBatchTuple, -} from '@op-engineering/op-sqlite'; + // openRemote, + // openSync, + type DB, + isLibsql, + open, + type SQLBatchTuple, +} from "@op-engineering/op-sqlite"; import { - expect, - afterEach, - beforeEach, - describe, - it, -} from '@op-engineering/op-test'; + afterEach, + beforeEach, + describe, + expect, + it, +} from "@op-engineering/op-test"; +import { chance } from "./utils"; + // import pkg from '../../package.json' -describe('Queries tests', () => { - let db: DB; - - beforeEach(async () => { - db = open({ - name: 'queries.sqlite', - encryptionKey: 'test', - }); - - await db.execute('DROP TABLE IF EXISTS User;'); - await db.execute('DROP TABLE IF EXISTS T1;'); - await db.execute('DROP TABLE IF EXISTS T2;'); - await db.execute( - 'CREATE TABLE User (id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL, nickname TEXT) STRICT;', - ); - }); - - afterEach(() => { - if (db) { - db.delete(); - // @ts-ignore - db = null; - } - }); - - if (isLibsql()) { - // itOnly('Remote open a turso database', async () => { - // const remoteDb = openRemote({ - // url: 'libsql://foo-ospfranco.turso.io', - // authToken: - // 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhIjoicnciLCJpYXQiOjE3MTY5NTc5OTUsImlkIjoiZmJkNzZmMjYtZTliYy00MGJiLTlmYmYtMDczZjFmMjdjOGY4In0.U3cAWBOvcdiqoPN3MB81sco7x8CGOjjtZ1ZEf30uo2iPcAmOuJzcnAznmDlZ6SpQd4qzuJxE4mAIoRlOkpzgBQ', - // }); - // console.log('Running select 1'); - // const res = await remoteDb.execute('SELECT 1'); - // console.log('after select 1;'); - // expect(res.rowsAffected).toEqual(0); - // }); - // it('Open a libsql database replicated to turso', async () => { - // const remoteDb = openSync({ - // url: 'libsql://foo-ospfranco.turso.io', - // authToken: - // 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhIjoicnciLCJpYXQiOjE3MTY5NTc5OTUsImlkIjoiZmJkNzZmMjYtZTliYy00MGJiLTlmYmYtMDczZjFmMjdjOGY4In0.U3cAWBOvcdiqoPN3MB81sco7x8CGOjjtZ1ZEf30uo2iPcAmOuJzcnAznmDlZ6SpQd4qzuJxE4mAIoRlOkpzgBQ', - // name: 'my replica', - // libsqlSyncInterval: 1000, - // encryptionKey: 'blah', - // }); - // const res = await remoteDb.execute('SELECT 1'); - // remoteDb.sync(); - // expect(res.rowsAffected).toEqual(0); - // }); - } - - it('Can create multiple connections to same db', async () => { - const db2 = open({ - name: 'queries.sqlite', - encryptionKey: 'test', - }); - - const db3 = open({ - name: 'queries.sqlite', - encryptionKey: 'test', - }); - - let promises = [ - db.execute('SELECT 1'), - db2.execute('SELECT 1'), - db3.execute('SELECT 1'), - ]; - - let res = await Promise.all(promises); - res.forEach(r => { - expect(r.rowsAffected).toEqual(0); - expect(r.rows[0]!['1']).toEqual(1); - }); - }); - - it('Trying to pass object as param should throw', async () => { - try { - // @ts-ignore - await db.execute('SELECT ?', [{foo: 'bar'}]); - } catch (e: any) { - expect( - e.message.includes( - 'Exception in HostFunction: Object is not an ArrayBuffer, cannot bind to SQLite', - ), - ).toEqual(true); - } - }); - - it('executeSync', () => { - const res = db.executeSync('SELECT 1'); - expect(res.rowsAffected).toEqual(0); - - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - const res2 = db.executeSync( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - expect(res2.rowsAffected).toEqual(1); - expect(res2.insertId).toEqual(1); - // expect(res2.rows).toBe([]); - expect(res2.rows?.length).toEqual(0); - }); - - it('Insert', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - const res = await db.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - // expect(res.metadata).toEqual([]); - expect(res.rows).toDeepEqual([]); - expect(res.rows?.length).toEqual(0); - }); - - it('Casts booleans to ints correctly', async () => { - await db.execute(`SELECT ?`, [1]); - await db.execute(`SELECT ?`, [true]); - }); - - it('Insert and query with host objects', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - const res = await db.executeWithHostObjects( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - // expect(res.metadata).toEqual([]); - expect(res.rows).toDeepEqual([]); - expect(res.rows?.length).toEqual(0); - - const queryRes = await db.executeWithHostObjects('SELECT * FROM User'); - - expect(queryRes.rowsAffected).toEqual(1); - expect(queryRes.insertId).toEqual(1); - expect(queryRes.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - }); - - it('Query without params', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const res = await db.execute('SELECT * FROM User'); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - expect(res.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - }); - - it('Query with params', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const res = await db.execute('SELECT * FROM User WHERE id = ?', [id]); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - expect(res.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - }); - - it('Query with sqlite functions', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - // COUNT(*) - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const countRes = await db.execute('SELECT COUNT(*) as count FROM User'); - - expect(countRes.rows?.length).toEqual(1); - expect(countRes.rows?.[0]?.count).toEqual(1); - - // SUM(age) - const id2 = chance.integer(); - const name2 = chance.name(); - const age2 = chance.integer(); - const networth2 = chance.floating(); - - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id2, name2, age2, networth2], - ); - - const sumRes = await db.execute('SELECT SUM(age) as sum FROM User;'); - - expect(sumRes.rows[0]!.sum).toEqual(age + age2); - - const maxRes = await db.execute('SELECT MAX(networth) as `max` FROM User;'); - const minRes = await db.execute('SELECT MIN(networth) as `min` FROM User;'); - const maxNetworth = Math.max(networth, networth2); - const minNetworth = Math.min(networth, networth2); - - expect(maxRes.rows[0]!.max).toEqual(maxNetworth); - expect(minRes.rows[0]!.min).toEqual(minNetworth); - }); - - it('Executes all the statements in a single string', async () => { - if (isLibsql()) { - return; - } - await db.execute( - `CREATE TABLE T1 ( id INT PRIMARY KEY) STRICT; +describe("Queries tests", () => { + let db: DB; + + beforeEach(async () => { + db = open({ + name: "queries.sqlite", + encryptionKey: "test", + }); + + await db.execute("DROP TABLE IF EXISTS User;"); + await db.execute("DROP TABLE IF EXISTS T1;"); + await db.execute("DROP TABLE IF EXISTS T2;"); + await db.execute( + "CREATE TABLE User (id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL, nickname TEXT) STRICT;", + ); + }); + + afterEach(() => { + if (db) { + db.delete(); + // @ts-expect-error + db = null; + } + }); + + if (isLibsql()) { + // itOnly('Remote open a turso database', async () => { + // const remoteDb = openRemote({ + // url: 'libsql://foo-ospfranco.turso.io', + // authToken: + // 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhIjoicnciLCJpYXQiOjE3MTY5NTc5OTUsImlkIjoiZmJkNzZmMjYtZTliYy00MGJiLTlmYmYtMDczZjFmMjdjOGY4In0.U3cAWBOvcdiqoPN3MB81sco7x8CGOjjtZ1ZEf30uo2iPcAmOuJzcnAznmDlZ6SpQd4qzuJxE4mAIoRlOkpzgBQ', + // }); + // console.log('Running select 1'); + // const res = await remoteDb.execute('SELECT 1'); + // console.log('after select 1;'); + // expect(res.rowsAffected).toEqual(0); + // }); + // it('Open a libsql database replicated to turso', async () => { + // const remoteDb = openSync({ + // url: 'libsql://foo-ospfranco.turso.io', + // authToken: + // 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhIjoicnciLCJpYXQiOjE3MTY5NTc5OTUsImlkIjoiZmJkNzZmMjYtZTliYy00MGJiLTlmYmYtMDczZjFmMjdjOGY4In0.U3cAWBOvcdiqoPN3MB81sco7x8CGOjjtZ1ZEf30uo2iPcAmOuJzcnAznmDlZ6SpQd4qzuJxE4mAIoRlOkpzgBQ', + // name: 'my replica', + // libsqlSyncInterval: 1000, + // encryptionKey: 'blah', + // }); + // const res = await remoteDb.execute('SELECT 1'); + // remoteDb.sync(); + // expect(res.rowsAffected).toEqual(0); + // }); + } + + it("Can create multiple connections to same db", async () => { + const db2 = open({ + name: "queries.sqlite", + encryptionKey: "test", + }); + + const db3 = open({ + name: "queries.sqlite", + encryptionKey: "test", + }); + + const promises = [ + db.execute("SELECT 1"), + db2.execute("SELECT 1"), + db3.execute("SELECT 1"), + ]; + + const res = await Promise.all(promises); + res.forEach((r) => { + expect(r.rowsAffected).toEqual(0); + expect(r.rows[0]?.["1"]).toEqual(1); + }); + }); + + it("Trying to pass object as param should throw", async () => { + try { + // @ts-expect-error + await db.execute("SELECT ?", [{ foo: "bar" }]); + } catch (e: any) { + expect( + e.message.includes( + "Exception in HostFunction: Object is not an ArrayBuffer, cannot bind to SQLite", + ), + ).toEqual(true); + } + }); + + it("executeSync", () => { + const res = db.executeSync("SELECT 1"); + expect(res.rowsAffected).toEqual(0); + + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + const res2 = db.executeSync( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + expect(res2.rowsAffected).toEqual(1); + expect(res2.insertId).toEqual(1); + // expect(res2.rows).toBe([]); + expect(res2.rows?.length).toEqual(0); + }); + + it("Insert", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + const res = await db.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + // expect(res.metadata).toEqual([]); + expect(res.rows).toDeepEqual([]); + expect(res.rows?.length).toEqual(0); + }); + + it("Casts booleans to ints correctly", async () => { + await db.execute(`SELECT ?`, [1]); + await db.execute(`SELECT ?`, [true]); + }); + + it("Insert and query with host objects", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + const res = await db.executeWithHostObjects( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + // expect(res.metadata).toEqual([]); + expect(res.rows).toDeepEqual([]); + expect(res.rows?.length).toEqual(0); + + const queryRes = await db.executeWithHostObjects("SELECT * FROM User"); + + expect(queryRes.rowsAffected).toEqual(1); + expect(queryRes.insertId).toEqual(1); + expect(queryRes.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + }); + + it("Query without params", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const res = await db.execute("SELECT * FROM User"); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + expect(res.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + }); + + it("Query with params", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const res = await db.execute("SELECT * FROM User WHERE id = ?", [id]); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + expect(res.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + }); + + it("Query with sqlite functions", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + // COUNT(*) + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const countRes = await db.execute("SELECT COUNT(*) as count FROM User"); + + expect(countRes.rows?.length).toEqual(1); + expect(countRes.rows?.[0]?.count).toEqual(1); + + // SUM(age) + const id2 = chance.integer(); + const name2 = chance.name(); + const age2 = chance.integer(); + const networth2 = chance.floating(); + + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id2, name2, age2, networth2], + ); + + const sumRes = await db.execute("SELECT SUM(age) as sum FROM User;"); + + expect(sumRes.rows[0]!.sum).toEqual(age + age2); + + const maxRes = await db.execute("SELECT MAX(networth) as `max` FROM User;"); + const minRes = await db.execute("SELECT MIN(networth) as `min` FROM User;"); + const maxNetworth = Math.max(networth, networth2); + const minNetworth = Math.min(networth, networth2); + + expect(maxRes.rows[0]!.max).toEqual(maxNetworth); + expect(minRes.rows[0]!.min).toEqual(minNetworth); + }); + + it("Executes all the statements in a single string", async () => { + if (isLibsql()) { + return; + } + await db.execute( + `CREATE TABLE T1 ( id INT PRIMARY KEY) STRICT; CREATE TABLE T2 ( id INT PRIMARY KEY) STRICT;`, - ); - - let t1name = await db.execute( - "SELECT name FROM sqlite_master WHERE type='table' AND name='T1';", - ); - - expect(t1name.rows[0]!.name).toEqual('T1'); - - let t2name = await db.execute( - "SELECT name FROM sqlite_master WHERE type='table' AND name='T2';", - ); - - expect(t2name.rows[0]!.name).toEqual('T2'); - }); - - it('Failed insert', async () => { - const id = chance.string(); - const name = chance.name(); - const age = chance.string(); - const networth = chance.string(); - try { - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - } catch (e: any) { - expect(typeof e).toEqual('object'); - - expect( - e.message.includes('cannot store TEXT value in INT column User.id'), - ).toEqual(true); - } - }); - - it('Transaction, auto commit', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.transaction(async tx => { - const res = await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - // expect(res.metadata).toEqual([]); - expect(res.rows).toDeepEqual([]); - expect(res.rows?.length).toEqual(0); - }); - - const res = await db.execute('SELECT * FROM User'); - expect(res.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - }); - - it('Transaction, manual commit', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.transaction(async tx => { - const res = await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - expect(res.rows).toDeepEqual([]); - expect(res.rows?.length).toEqual(0); - - await tx.commit(); - }); - - const res = await db.execute('SELECT * FROM User'); - // console.log(res); - expect(res.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - }); - - it('Transaction, executed in order', async () => { - const xs = 10; - const actual: unknown[] = []; - - // ARRANGE: Generate expected data - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - - // ACT: Start multiple transactions to upsert and select the same record - const promises = []; - for (let i = 1; i <= xs; i++) { - const promised = db.transaction(async tx => { - // ACT: Upsert statement to create record / increment the value - await tx.execute( - ` + ); + + const t1name = await db.execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name='T1';", + ); + + expect(t1name.rows[0]!.name).toEqual("T1"); + + const t2name = await db.execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name='T2';", + ); + + expect(t2name.rows[0]!.name).toEqual("T2"); + }); + + it("Failed insert", async () => { + const id = chance.string(); + const name = chance.name(); + const age = chance.string(); + const networth = chance.string(); + try { + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + } catch (e: any) { + expect(typeof e).toEqual("object"); + + expect( + e.message.includes("cannot store TEXT value in INT column User.id"), + ).toEqual(true); + } + }); + + it("Transaction, auto commit", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.transaction(async (tx) => { + const res = await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + // expect(res.metadata).toEqual([]); + expect(res.rows).toDeepEqual([]); + expect(res.rows?.length).toEqual(0); + }); + + const res = await db.execute("SELECT * FROM User"); + expect(res.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + }); + + it("Transaction, manual commit", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.transaction(async (tx) => { + const res = await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + expect(res.rows).toDeepEqual([]); + expect(res.rows?.length).toEqual(0); + + await tx.commit(); + }); + + const res = await db.execute("SELECT * FROM User"); + // console.log(res); + expect(res.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + }); + + it("Transaction, executed in order", async () => { + const xs = 10; + const actual: unknown[] = []; + + // ARRANGE: Generate expected data + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + + // ACT: Start multiple transactions to upsert and select the same record + const promises = []; + for (let i = 1; i <= xs; i++) { + const promised = db.transaction(async (tx) => { + // ACT: Upsert statement to create record / increment the value + await tx.execute( + ` INSERT OR REPLACE INTO [User] ([id], [name], [age], [networth]) SELECT ?, ?, ?, IFNULL(( @@ -397,429 +398,427 @@ describe('Queries tests', () => { WHERE [id] = ? ), 0) `, - [id, name, age, id], - ); - - // ACT: Select statement to get incremented value and store it for checking later - const results = await tx.execute( - 'SELECT [networth] FROM [User] WHERE [id] = ?', - [id], - ); - - actual.push(results.rows[0]!.networth); - }); - - promises.push(promised); - } - - // ACT: Wait for all transactions to complete - await Promise.all(promises); - - // ASSERT: That the expected values where returned - const expected = Array(xs) - .fill(0) - .map((_, index) => index * 1000); - - expect(actual).toDeepEqual(expected); - }); - - it('Transaction, cannot execute after commit', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.transaction(async tx => { - const res = await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - // expect(res.metadata).toEqual([]); - expect(res.rows).toDeepEqual([]); - expect(res.rows.length).toEqual(0); - - await tx.commit(); - - try { - await tx.execute('SELECT * FROM "User"'); - } catch (e) { - expect(!!e).toEqual(true); - } - }); - - const res = await db.execute('SELECT * FROM User'); - expect(res.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - }); - - it('Incorrect transaction, manual rollback', async () => { - const id = chance.string(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.transaction(async tx => { - try { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - } catch (e) { - await tx.rollback(); - } - }); - - const res = await db.execute('SELECT * FROM User'); - expect(res.rows).toDeepEqual([]); - }); - - it('Correctly throws', async () => { - const id = chance.string(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - try { - await db.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - } catch (e: any) { - expect(!!e).toEqual(true); - } - }); - - it('Rollback', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - await tx.rollback(); - const res = await db.execute('SELECT * FROM User'); - expect(res.rows).toDeepEqual([]); - }); - }); - - it('Execute raw sync should return just an array of objects', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const res = db.executeRawSync('SELECT id, name, age, networth FROM User'); - expect(res).toDeepEqual([[id, name, age, networth]]); - }); - - it('Transaction, rejects on callback error', async () => { - const promised = db.transaction(() => { - throw new Error('Error from callback'); - }); - - // ASSERT: should return a promise that eventually rejects - expect(typeof promised == 'object'); - try { - await promised; - // expect.fail('Should not resolve'); - } catch (e) { - // expect(e).to.be.a.instanceof(Error); - expect((e as Error)?.message).toEqual('Error from callback'); - } - }); - - it('Transaction, rejects on invalid query', async () => { - const promised = db.transaction(async tx => { - await tx.execute('SELECT * FROM [tableThatDoesNotExist];'); - }); - - // ASSERT: should return a promise that eventually rejects - // expect(promised).to.have.property('then').that.is.a('function'); - try { - await promised; - // expect.fail('Should not resolve'); - } catch (e) { - // expect(e).to.be.a.instanceof(Error); - expect( - (e as Error)?.message.includes('no such table: tableThatDoesNotExist'), - ).toBe(true); - } - }); - - it('Transaction, handle async callback', async () => { - let ranCallback = false; - const promised = db.transaction(async tx => { - await new Promise(done => { - setTimeout(() => done(), 50); - }); - tx.execute('SELECT * FROM User;'); - ranCallback = true; - }); - - // ASSERT: should return a promise that eventually rejects - // expect(promised).to.have.property('then').that.is.a('function'); - await promised; - expect(ranCallback).toEqual(true); - }); - - it('executeBatch', async () => { - const id1 = chance.integer(); - const name1 = chance.name(); - const age1 = chance.integer(); - const networth1 = chance.floating(); - - const id2 = chance.integer(); - const name2 = chance.name(); - const age2 = chance.integer(); - const networth2 = chance.floating(); - - const commands: SQLBatchTuple[] = [ - ['SELECT * FROM "User"', []], - ['SELECT * FROM "User"'], - [ - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id1, name1, age1, networth1], - ], - [ - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [[id2, name2, age2, networth2]], - ], - ]; - - await db.executeBatch(commands); - - const res = await db.execute('SELECT * FROM User'); - - expect(res.rows).toDeepEqual([ - {id: id1, name: name1, age: age1, networth: networth1, nickname: null}, - { - id: id2, - name: name2, - age: age2, - networth: networth2, - nickname: null, - }, - ]); - }); - - it('Batch execute with BLOB', async () => { - let db = open({ - name: 'queries.sqlite', - encryptionKey: 'test', - }); - - await db.execute('DROP TABLE IF EXISTS User;'); - await db.execute( - 'CREATE TABLE IF NOT EXISTS User (id TEXT PRIMARY KEY NOT NULL, name TEXT NOT NULL, age INT, networth BLOB, nickname TEXT) STRICT;', - ); - const id1 = '1'; - const name1 = 'name1'; - const age1 = 12; - const networth1 = new Uint8Array([1, 2, 3]); - - const id2 = '2'; - const name2 = 'name2'; - const age2 = 17; - const networth2 = new Uint8Array([3, 2, 1]); - - const commands: SQLBatchTuple[] = [ - [ - 'INSERT OR REPLACE INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id1, name1, age1, networth1], - ], - [ - 'INSERT OR REPLACE INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [[id2, name2, age2, networth2]], - ], - ]; - - // bomb~ (NOBRIDGE) ERROR Error: Exception in HostFunction: - await db.executeBatch(commands); - }); - - it('DumbHostObject allows to write known props', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const res = await db.executeWithHostObjects('SELECT * FROM User'); - - expect(res.insertId).toEqual(1); - expect(res.rows).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - - res.rows[0]!.name = 'quack_changed'; - - expect(res.rows[0]!.name).toEqual('quack_changed'); - }); - - it('DumbHostObject allows to write new props', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const res = await db.executeWithHostObjects('SELECT * FROM User'); - - expect(res.rowsAffected).toEqual(1); - expect(res.insertId).toEqual(1); - expect(res.rows!).toDeepEqual([ - { - id, - name, - age, - networth, - nickname: null, - }, - ]); - - res.rows[0]!.myWeirdProp = 'quack_changed'; - - expect(res.rows[0]!.myWeirdProp).toEqual('quack_changed'); - }); - - it('Execute raw should return just an array of objects', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - await db.execute( - 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - - const res = await db.executeRaw('SELECT id, name, age, networth FROM User'); - expect(res).toDeepEqual([[id, name, age, networth]]); - }); - - it('Create fts5 virtual table', async () => { - await db.execute( - 'CREATE VIRTUAL TABLE fts5_table USING fts5(name, content);', - ); - await db.execute('INSERT INTO fts5_table (name, content) VALUES(?, ?)', [ - 'test', - 'test content', - ]); - - const res = await db.execute('SELECT * FROM fts5_table'); - expect(res.rows).toDeepEqual([{name: 'test', content: 'test content'}]); - }); - - it('Various queries', async () => { - await db.execute('SELECT 1 '); - await db.execute('SELECT 1 '); - await db.execute('SELECT 1; ', []); - await db.execute('SELECT ?; ', [1]); - }); - - it('Handles concurrent transactions correctly', async () => { - const id = chance.integer(); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - const transaction1 = db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id, name, age, networth], - ); - }); - - const transaction2 = db.transaction(async tx => { - await tx.execute( - 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', - [id + 1, name, age, networth], - ); - }); - - await Promise.all([transaction1, transaction2]); - - const res = await db.execute('SELECT * FROM User'); - expect(res.rows.length).toEqual(2); - }); - - it('Pragma user_version', () => { - const res = db.executeSync('PRAGMA user_version'); - expect(res.rows).toDeepEqual([{user_version: 0}]); - }); - - -// const sqliteVecEnabled = pkg?.['op-sqlite']?.sqliteVec === true; -// if (sqliteVecEnabled) { -// it('sqlite-vec extension: vector similarity search', async () => { -// // Create a virtual table for storing vectors -// await db.execute(` -// CREATE VIRTUAL TABLE vec_items USING vec0( -// embedding FLOAT[8] -// ) -// `); - -// // Insert some sample vectors -// await db.execute(` -// INSERT INTO vec_items(rowid, embedding) -// VALUES -// (1, '[-0.200, 0.250, 0.341, -0.211, 0.645, 0.935, -0.316, -0.924]'), -// (2, '[0.443, -0.501, 0.355, -0.771, 0.707, -0.708, -0.185, 0.362]'), -// (3, '[0.716, -0.927, 0.134, 0.052, -0.669, 0.793, -0.634, -0.162]'), -// (4, '[-0.710, 0.330, 0.656, 0.041, -0.990, 0.726, 0.385, -0.958]') -// `); - -// // Perform KNN query to find the 2 nearest neighbors -// const queryVector = '[0.890, 0.544, 0.825, 0.961, 0.358, 0.0196, 0.521, 0.175]'; -// const result = await db.execute(` -// SELECT rowid, distance -// FROM vec_items -// WHERE embedding MATCH ? -// ORDER BY distance -// LIMIT 2 -// `, [queryVector]); - -// // Verify results -// expect(result.rows.length).toEqual(2); -// expect(result.rows[0]!.rowid).toEqual(2); -// expect(result.rows[1]!.rowid).toEqual(1); - -// // Verify distances are positive numbers -// const distance0 = result.rows[0]!.distance as number; -// const distance1 = result.rows[1]!.distance as number; -// expect(typeof distance0).toEqual('number'); -// expect(distance0 > 0).toBeTruthy(); -// expect(distance1 > 0).toBeTruthy(); -// }); -// } - + [id, name, age, id], + ); + + // ACT: Select statement to get incremented value and store it for checking later + const results = await tx.execute( + "SELECT [networth] FROM [User] WHERE [id] = ?", + [id], + ); + + actual.push(results.rows[0]!.networth); + }); + + promises.push(promised); + } + + // ACT: Wait for all transactions to complete + await Promise.all(promises); + + // ASSERT: That the expected values where returned + const expected = Array(xs) + .fill(0) + .map((_, index) => index * 1000); + + expect(actual).toDeepEqual(expected); + }); + + it("Transaction, cannot execute after commit", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.transaction(async (tx) => { + const res = await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + // expect(res.metadata).toEqual([]); + expect(res.rows).toDeepEqual([]); + expect(res.rows.length).toEqual(0); + + await tx.commit(); + + try { + await tx.execute('SELECT * FROM "User"'); + } catch (e) { + expect(!!e).toEqual(true); + } + }); + + const res = await db.execute("SELECT * FROM User"); + expect(res.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + }); + + it("Incorrect transaction, manual rollback", async () => { + const id = chance.string(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.transaction(async (tx) => { + try { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + } catch (_e) { + await tx.rollback(); + } + }); + + const res = await db.execute("SELECT * FROM User"); + expect(res.rows).toDeepEqual([]); + }); + + it("Correctly throws", async () => { + const id = chance.string(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + try { + await db.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + } catch (e: any) { + expect(!!e).toEqual(true); + } + }); + + it("Rollback", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + await tx.rollback(); + const res = await db.execute("SELECT * FROM User"); + expect(res.rows).toDeepEqual([]); + }); + }); + + it("Execute raw sync should return just an array of objects", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const res = db.executeRawSync("SELECT id, name, age, networth FROM User"); + expect(res).toDeepEqual([[id, name, age, networth]]); + }); + + it("Transaction, rejects on callback error", async () => { + const promised = db.transaction(() => { + throw new Error("Error from callback"); + }); + + // ASSERT: should return a promise that eventually rejects + expect(typeof promised === "object"); + try { + await promised; + // expect.fail('Should not resolve'); + } catch (e) { + // expect(e).to.be.a.instanceof(Error); + expect((e as Error)?.message).toEqual("Error from callback"); + } + }); + + it("Transaction, rejects on invalid query", async () => { + const promised = db.transaction(async (tx) => { + await tx.execute("SELECT * FROM [tableThatDoesNotExist];"); + }); + + // ASSERT: should return a promise that eventually rejects + // expect(promised).to.have.property('then').that.is.a('function'); + try { + await promised; + // expect.fail('Should not resolve'); + } catch (e) { + // expect(e).to.be.a.instanceof(Error); + expect( + (e as Error)?.message.includes("no such table: tableThatDoesNotExist"), + ).toBe(true); + } + }); + + it("Transaction, handle async callback", async () => { + let ranCallback = false; + const promised = db.transaction(async (tx) => { + await new Promise((done) => { + setTimeout(() => done(), 50); + }); + tx.execute("SELECT * FROM User;"); + ranCallback = true; + }); + + // ASSERT: should return a promise that eventually rejects + // expect(promised).to.have.property('then').that.is.a('function'); + await promised; + expect(ranCallback).toEqual(true); + }); + + it("executeBatch", async () => { + const id1 = chance.integer(); + const name1 = chance.name(); + const age1 = chance.integer(); + const networth1 = chance.floating(); + + const id2 = chance.integer(); + const name2 = chance.name(); + const age2 = chance.integer(); + const networth2 = chance.floating(); + + const commands: SQLBatchTuple[] = [ + ['SELECT * FROM "User"', []], + ['SELECT * FROM "User"'], + [ + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id1, name1, age1, networth1], + ], + [ + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [[id2, name2, age2, networth2]], + ], + ]; + + await db.executeBatch(commands); + + const res = await db.execute("SELECT * FROM User"); + + expect(res.rows).toDeepEqual([ + { id: id1, name: name1, age: age1, networth: networth1, nickname: null }, + { + id: id2, + name: name2, + age: age2, + networth: networth2, + nickname: null, + }, + ]); + }); + + it("Batch execute with BLOB", async () => { + const db = open({ + name: "queries.sqlite", + encryptionKey: "test", + }); + + await db.execute("DROP TABLE IF EXISTS User;"); + await db.execute( + "CREATE TABLE IF NOT EXISTS User (id TEXT PRIMARY KEY NOT NULL, name TEXT NOT NULL, age INT, networth BLOB, nickname TEXT) STRICT;", + ); + const id1 = "1"; + const name1 = "name1"; + const age1 = 12; + const networth1 = new Uint8Array([1, 2, 3]); + + const id2 = "2"; + const name2 = "name2"; + const age2 = 17; + const networth2 = new Uint8Array([3, 2, 1]); + + const commands: SQLBatchTuple[] = [ + [ + 'INSERT OR REPLACE INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id1, name1, age1, networth1], + ], + [ + 'INSERT OR REPLACE INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [[id2, name2, age2, networth2]], + ], + ]; + + // bomb~ (NOBRIDGE) ERROR Error: Exception in HostFunction: + await db.executeBatch(commands); + }); + + it("DumbHostObject allows to write known props", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const res = await db.executeWithHostObjects("SELECT * FROM User"); + + expect(res.insertId).toEqual(1); + expect(res.rows).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + + res.rows[0]!.name = "quack_changed"; + + expect(res.rows[0]!.name).toEqual("quack_changed"); + }); + + it("DumbHostObject allows to write new props", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const res = await db.executeWithHostObjects("SELECT * FROM User"); + + expect(res.rowsAffected).toEqual(1); + expect(res.insertId).toEqual(1); + expect(res.rows!).toDeepEqual([ + { + id, + name, + age, + networth, + nickname: null, + }, + ]); + + res.rows[0]!.myWeirdProp = "quack_changed"; + + expect(res.rows[0]!.myWeirdProp).toEqual("quack_changed"); + }); + + it("Execute raw should return just an array of objects", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + await db.execute( + "INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)", + [id, name, age, networth], + ); + + const res = await db.executeRaw("SELECT id, name, age, networth FROM User"); + expect(res).toDeepEqual([[id, name, age, networth]]); + }); + + it("Create fts5 virtual table", async () => { + await db.execute( + "CREATE VIRTUAL TABLE fts5_table USING fts5(name, content);", + ); + await db.execute("INSERT INTO fts5_table (name, content) VALUES(?, ?)", [ + "test", + "test content", + ]); + + const res = await db.execute("SELECT * FROM fts5_table"); + expect(res.rows).toDeepEqual([{ name: "test", content: "test content" }]); + }); + + it("Various queries", async () => { + await db.execute("SELECT 1 "); + await db.execute("SELECT 1 "); + await db.execute("SELECT 1; ", []); + await db.execute("SELECT ?; ", [1]); + }); + + it("Handles concurrent transactions correctly", async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + const transaction1 = db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + }); + + const transaction2 = db.transaction(async (tx) => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id + 1, name, age, networth], + ); + }); + + await Promise.all([transaction1, transaction2]); + + const res = await db.execute("SELECT * FROM User"); + expect(res.rows.length).toEqual(2); + }); + + it("Pragma user_version", () => { + const res = db.executeSync("PRAGMA user_version"); + expect(res.rows).toDeepEqual([{ user_version: 0 }]); + }); + + // const sqliteVecEnabled = pkg?.['op-sqlite']?.sqliteVec === true; + // if (sqliteVecEnabled) { + // it('sqlite-vec extension: vector similarity search', async () => { + // // Create a virtual table for storing vectors + // await db.execute(` + // CREATE VIRTUAL TABLE vec_items USING vec0( + // embedding FLOAT[8] + // ) + // `); + + // // Insert some sample vectors + // await db.execute(` + // INSERT INTO vec_items(rowid, embedding) + // VALUES + // (1, '[-0.200, 0.250, 0.341, -0.211, 0.645, 0.935, -0.316, -0.924]'), + // (2, '[0.443, -0.501, 0.355, -0.771, 0.707, -0.708, -0.185, 0.362]'), + // (3, '[0.716, -0.927, 0.134, 0.052, -0.669, 0.793, -0.634, -0.162]'), + // (4, '[-0.710, 0.330, 0.656, 0.041, -0.990, 0.726, 0.385, -0.958]') + // `); + + // // Perform KNN query to find the 2 nearest neighbors + // const queryVector = '[0.890, 0.544, 0.825, 0.961, 0.358, 0.0196, 0.521, 0.175]'; + // const result = await db.execute(` + // SELECT rowid, distance + // FROM vec_items + // WHERE embedding MATCH ? + // ORDER BY distance + // LIMIT 2 + // `, [queryVector]); + + // // Verify results + // expect(result.rows.length).toEqual(2); + // expect(result.rows[0]!.rowid).toEqual(2); + // expect(result.rows[1]!.rowid).toEqual(1); + + // // Verify distances are positive numbers + // const distance0 = result.rows[0]!.distance as number; + // const distance1 = result.rows[1]!.distance as number; + // expect(typeof distance0).toEqual('number'); + // expect(distance0 > 0).toBeTruthy(); + // expect(distance1 > 0).toBeTruthy(); + // }); + // } }); diff --git a/example/src/tests/reactive.ts b/example/src/tests/reactive.ts index 1804ca07..aeedbc49 100644 --- a/example/src/tests/reactive.ts +++ b/example/src/tests/reactive.ts @@ -1,329 +1,329 @@ -import {isLibsql, open, type DB} from '@op-engineering/op-sqlite'; +import { type DB, isLibsql, open } from "@op-engineering/op-sqlite"; import { - afterAll, - beforeEach, - describe, - it, - expect, -} from '@op-engineering/op-test'; -import {sleep} from './utils'; -import Chance from 'chance'; + afterAll, + beforeEach, + describe, + expect, + it, +} from "@op-engineering/op-test"; +import Chance from "chance"; +import { sleep } from "./utils"; const chance = new Chance(); -describe('Reactive queries', () => { - let db: DB; - beforeEach(async () => { - if (db) { - db.close(); - db.delete(); - } - - db = open({ - name: 'reactive.sqlite', - encryptionKey: 'test', - }); - - await db.execute('DROP TABLE IF EXISTS User;'); - await db.execute( - 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL, nickname TEXT) STRICT;', - ); - }); - - afterAll(() => { - if (db) { - db.close(); - db.delete(); - // @ts-ignore - db = null; - } - }); - // libsql does not support reactive queries - if (isLibsql()) { - return; - } - - it('Table reactive query', async () => { - let fullSelectRan = false; - let emittedUser = null; - const unsubscribe = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - }, - ], - callback: _data => { - fullSelectRan = true; - }, - }); - - const unsubscribe2 = db.reactiveExecute({ - query: 'SELECT name FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - }, - ], - callback: data => { - emittedUser = data.rows[0]; - }, - }); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);', - [1, 'John', 30, 1000, 'Johnny'], - ); - }); - - await sleep(20); - - await db.transaction(async tx => { - await tx.execute('UPDATE User SET name = ? WHERE id = ?;', ['Foo', 1]); - }); - - await sleep(20); - - expect(!!fullSelectRan).toBe(true); - expect(emittedUser).toDeepEqual({ - name: 'Foo', - }); - - unsubscribe(); - unsubscribe2(); - }); - - it('Can unsubscribe from reactive query', async () => { - let emittedCount = 0; - const unsubscribe = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - }, - ], - callback: () => { - emittedCount++; - }, - }); - - // expect(unsubscribe).to.be.a('function'); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);', - [1, 'John', 30, 1000, 'Johnny'], - ); - }); - - await sleep(20); - - unsubscribe(); - - await db.transaction(async tx => { - await tx.execute('UPDATE User SET name = ? WHERE id = ?;', ['Foo', 1]); - }); - await sleep(20); - expect(emittedCount).toEqual(1); - }); - - it('Row reactive query', async () => { - let firstReactiveRan = false; - let secondReactiveRan = false; - let emittedUser = null; - - const unsubscribe = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - ids: [2], - }, - ], - callback: () => { - firstReactiveRan = true; - }, - }); - - const unsubscribe2 = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'Foo', - }, - ], - callback: () => { - secondReactiveRan = true; - }, - }); - - const unsubscribe3 = db.reactiveExecute({ - query: 'SELECT name FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - ids: [1], - }, - ], - callback: data => { - emittedUser = data.rows[0]; - }, - }); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);', - [1, 'John', 30, 1000, 'Johnny'], - ); - }); - - await sleep(0); - - await db.transaction(async tx => { - await tx.execute('UPDATE User SET name = ? WHERE id = ?;', ['Foo', 1]); - }); - - await sleep(0); - - expect(!!firstReactiveRan).toBe(false); - expect(!!secondReactiveRan).toBe(false); - expect(emittedUser).toDeepEqual({ - name: 'Foo', - }); - unsubscribe(); - unsubscribe2(); - unsubscribe3(); - }); - - it('Row reactive query with executeBatch', async () => { - let firstReactiveRan = false; - let secondReactiveRan = false; - let emittedUser = null; - - const unsubscribe = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - ids: [2], - }, - ], - callback: () => { - firstReactiveRan = true; - }, - }); - - const unsubscribe2 = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'Foo', - }, - ], - callback: () => { - secondReactiveRan = true; - }, - }); - - const unsubscribe3 = db.reactiveExecute({ - query: 'SELECT name FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - ids: [1], - }, - ], - callback: data => { - emittedUser = data.rows[0]; - }, - }); - - await db.executeBatch([ - [ - 'INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);', - [1, 'John', 30, 1000, 'Johnny'], - ], - ]); - - await sleep(0); - - await db.transaction(async tx => { - await tx.execute('UPDATE User SET name = ? WHERE id = ?;', ['Foo', 1]); - }); - - await sleep(0); - - expect(!!firstReactiveRan).toBe(false); - expect(!!secondReactiveRan).toBe(false); - expect(emittedUser).toDeepEqual({ - name: 'Foo', - }); - unsubscribe(); - unsubscribe2(); - unsubscribe3(); - }); - - it('Update hook and reactive queries work at the same time', async () => { - let promiseResolve: any; - let promise = new Promise(resolve => { - promiseResolve = resolve; - }); - db.updateHook(({operation}) => { - promiseResolve?.(operation); - }); - - let emittedUser = null; - const unsubscribe = db.reactiveExecute({ - query: 'SELECT * FROM User;', - arguments: [], - fireOn: [ - { - table: 'User', - }, - ], - callback: data => { - emittedUser = data.rows[0]; - }, - }); - - const id = chance.integer({ - min: 1, - max: 100000, - }); - const name = chance.name(); - const age = chance.integer(); - const networth = chance.floating(); - - await db.transaction(async tx => { - await tx.execute( - 'INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);', - [id, name, age, networth, 'Johnny'], - ); - }); - - const operation = await promise; - - await sleep(0); - - expect(operation).toEqual('INSERT'); - expect(emittedUser).toDeepEqual({ - id, - name, - age, - networth, - nickname: 'Johnny', - }); - - unsubscribe(); - }); +describe("Reactive queries", () => { + let db: DB; + beforeEach(async () => { + if (db) { + db.close(); + db.delete(); + } + + db = open({ + name: "reactive.sqlite", + encryptionKey: "test", + }); + + await db.execute("DROP TABLE IF EXISTS User;"); + await db.execute( + "CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL, nickname TEXT) STRICT;", + ); + }); + + afterAll(() => { + if (db) { + db.close(); + db.delete(); + // @ts-expect-error + db = null; + } + }); + // libsql does not support reactive queries + if (isLibsql()) { + return; + } + + it("Table reactive query", async () => { + let fullSelectRan = false; + let emittedUser = null; + const unsubscribe = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + }, + ], + callback: (_data) => { + fullSelectRan = true; + }, + }); + + const unsubscribe2 = db.reactiveExecute({ + query: "SELECT name FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + }, + ], + callback: (data) => { + emittedUser = data.rows[0]; + }, + }); + + await db.transaction(async (tx) => { + await tx.execute( + "INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);", + [1, "John", 30, 1000, "Johnny"], + ); + }); + + await sleep(20); + + await db.transaction(async (tx) => { + await tx.execute("UPDATE User SET name = ? WHERE id = ?;", ["Foo", 1]); + }); + + await sleep(20); + + expect(!!fullSelectRan).toBe(true); + expect(emittedUser).toDeepEqual({ + name: "Foo", + }); + + unsubscribe(); + unsubscribe2(); + }); + + it("Can unsubscribe from reactive query", async () => { + let emittedCount = 0; + const unsubscribe = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + }, + ], + callback: () => { + emittedCount++; + }, + }); + + // expect(unsubscribe).to.be.a('function'); + + await db.transaction(async (tx) => { + await tx.execute( + "INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);", + [1, "John", 30, 1000, "Johnny"], + ); + }); + + await sleep(20); + + unsubscribe(); + + await db.transaction(async (tx) => { + await tx.execute("UPDATE User SET name = ? WHERE id = ?;", ["Foo", 1]); + }); + await sleep(20); + expect(emittedCount).toEqual(1); + }); + + it("Row reactive query", async () => { + let firstReactiveRan = false; + let secondReactiveRan = false; + let emittedUser = null; + + const unsubscribe = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + ids: [2], + }, + ], + callback: () => { + firstReactiveRan = true; + }, + }); + + const unsubscribe2 = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "Foo", + }, + ], + callback: () => { + secondReactiveRan = true; + }, + }); + + const unsubscribe3 = db.reactiveExecute({ + query: "SELECT name FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + ids: [1], + }, + ], + callback: (data) => { + emittedUser = data.rows[0]; + }, + }); + + await db.transaction(async (tx) => { + await tx.execute( + "INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);", + [1, "John", 30, 1000, "Johnny"], + ); + }); + + await sleep(0); + + await db.transaction(async (tx) => { + await tx.execute("UPDATE User SET name = ? WHERE id = ?;", ["Foo", 1]); + }); + + await sleep(0); + + expect(!!firstReactiveRan).toBe(false); + expect(!!secondReactiveRan).toBe(false); + expect(emittedUser).toDeepEqual({ + name: "Foo", + }); + unsubscribe(); + unsubscribe2(); + unsubscribe3(); + }); + + it("Row reactive query with executeBatch", async () => { + let firstReactiveRan = false; + let secondReactiveRan = false; + let emittedUser = null; + + const unsubscribe = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + ids: [2], + }, + ], + callback: () => { + firstReactiveRan = true; + }, + }); + + const unsubscribe2 = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "Foo", + }, + ], + callback: () => { + secondReactiveRan = true; + }, + }); + + const unsubscribe3 = db.reactiveExecute({ + query: "SELECT name FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + ids: [1], + }, + ], + callback: (data) => { + emittedUser = data.rows[0]; + }, + }); + + await db.executeBatch([ + [ + "INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);", + [1, "John", 30, 1000, "Johnny"], + ], + ]); + + await sleep(0); + + await db.transaction(async (tx) => { + await tx.execute("UPDATE User SET name = ? WHERE id = ?;", ["Foo", 1]); + }); + + await sleep(0); + + expect(!!firstReactiveRan).toBe(false); + expect(!!secondReactiveRan).toBe(false); + expect(emittedUser).toDeepEqual({ + name: "Foo", + }); + unsubscribe(); + unsubscribe2(); + unsubscribe3(); + }); + + it("Update hook and reactive queries work at the same time", async () => { + let promiseResolve: any; + const promise = new Promise((resolve) => { + promiseResolve = resolve; + }); + db.updateHook(({ operation }) => { + promiseResolve?.(operation); + }); + + let emittedUser = null; + const unsubscribe = db.reactiveExecute({ + query: "SELECT * FROM User;", + arguments: [], + fireOn: [ + { + table: "User", + }, + ], + callback: (data) => { + emittedUser = data.rows[0]; + }, + }); + + const id = chance.integer({ + min: 1, + max: 100000, + }); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + await db.transaction(async (tx) => { + await tx.execute( + "INSERT INTO User (id, name, age, networth, nickname) VALUES (?, ?, ?, ?, ?);", + [id, name, age, networth, "Johnny"], + ); + }); + + const operation = await promise; + + await sleep(0); + + expect(operation).toEqual("INSERT"); + expect(emittedUser).toDeepEqual({ + id, + name, + age, + networth, + nickname: "Johnny", + }); + + unsubscribe(); + }); }); diff --git a/example/src/tests/storage.ts b/example/src/tests/storage.ts index e6542b36..086218bd 100644 --- a/example/src/tests/storage.ts +++ b/example/src/tests/storage.ts @@ -1,68 +1,68 @@ -import {Storage} from '@op-engineering/op-sqlite'; +import { Storage } from "@op-engineering/op-sqlite"; import { - beforeEach, - describe, - it, - afterEach, - expect, -} from '@op-engineering/op-test'; + afterEach, + beforeEach, + describe, + expect, + it, +} from "@op-engineering/op-test"; -describe('Storage', () => { - let storage: Storage; - beforeEach(() => { - storage = new Storage({encryptionKey: 'test'}); - }); +describe("Storage", () => { + let storage: Storage; + beforeEach(() => { + storage = new Storage({ encryptionKey: "test" }); + }); - afterEach(() => { - storage.delete(); - }); + afterEach(() => { + storage.delete(); + }); - it('Can set and get sync', async () => { - storage.setItemSync('foo', 'bar'); - const res = storage.getItemSync('foo'); - expect(res).toEqual('bar'); - }); + it("Can set and get sync", async () => { + storage.setItemSync("foo", "bar"); + const res = storage.getItemSync("foo"); + expect(res).toEqual("bar"); + }); - it('Can set and get async', async () => { - await storage.setItem('quack', 'bark'); - const res = await storage.getItem('quack'); - expect(res).toEqual('bark'); - }); + it("Can set and get async", async () => { + await storage.setItem("quack", "bark"); + const res = await storage.getItem("quack"); + expect(res).toEqual("bark"); + }); - it('can remove item sync', async () => { - storage.setItemSync('foo', 'bar'); - storage.removeItemSync('foo'); - const res = storage.getItemSync('foo'); - expect(res).toEqual(undefined); - }); + it("can remove item sync", async () => { + storage.setItemSync("foo", "bar"); + storage.removeItemSync("foo"); + const res = storage.getItemSync("foo"); + expect(res).toEqual(undefined); + }); - it('can remove item async', async () => { - await storage.setItem('quack', 'bark'); - await storage.removeItem('quack'); - const res = await storage.getItem('quack'); - expect(res).toEqual(undefined); - }); + it("can remove item async", async () => { + await storage.setItem("quack", "bark"); + await storage.removeItem("quack"); + const res = await storage.getItem("quack"); + expect(res).toEqual(undefined); + }); - it('can clear', async () => { - await storage.setItem('quack', 'bark'); - await storage.setItem('quack2', 'bark'); - await storage.clear(); - const res = await storage.getItem('quack'); - expect(res).toEqual(undefined); - }); + it("can clear", async () => { + await storage.setItem("quack", "bark"); + await storage.setItem("quack2", "bark"); + await storage.clear(); + const res = await storage.getItem("quack"); + expect(res).toEqual(undefined); + }); - it('can clear sync', async () => { - storage.setItemSync('quack', 'bark'); - storage.setItemSync('quack2', 'bark'); - storage.clearSync(); - const res = storage.getItemSync('quack'); - expect(res).toEqual(undefined); - }); + it("can clear sync", async () => { + storage.setItemSync("quack", "bark"); + storage.setItemSync("quack2", "bark"); + storage.clearSync(); + const res = storage.getItemSync("quack"); + expect(res).toEqual(undefined); + }); - it('can get all keys', async () => { - await storage.setItem('quack', 'bark'); - await storage.setItem('quack2', 'bark'); - const keys = storage.getAllKeys(); - expect(keys).toDeepEqual(['quack', 'quack2']); - }); + it("can get all keys", async () => { + await storage.setItem("quack", "bark"); + await storage.setItem("quack2", "bark"); + const keys = storage.getAllKeys(); + expect(keys).toDeepEqual(["quack", "quack2"]); + }); }); diff --git a/example/src/tests/utils.ts b/example/src/tests/utils.ts index df4b169e..cf5dd279 100644 --- a/example/src/tests/utils.ts +++ b/example/src/tests/utils.ts @@ -1,8 +1,8 @@ -import Chance from 'chance'; +import Chance from "chance"; export async function sleep(ms: number): Promise { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); } export const chance = new Chance(); diff --git a/example/src/tests/web.ts b/example/src/tests/web.ts index 927d1101..892271a8 100644 --- a/example/src/tests/web.ts +++ b/example/src/tests/web.ts @@ -1,80 +1,86 @@ -import {open, openAsync, type DB} from '@op-engineering/op-sqlite'; -import {describe, expect, it} from '@op-engineering/op-test'; -import {Platform} from 'react-native'; - -describe('Web backend', () => { - if (Platform.OS !== 'web') { - return; - } - - it('opens with openAsync and executes queries', async () => { - const db = await openAsync({ - name: 'web-tests.sqlite', - }); - - await db.execute( - 'CREATE TABLE IF NOT EXISTS WebTests (id INTEGER PRIMARY KEY, value TEXT NOT NULL)', - ); - await db.execute('DELETE FROM WebTests'); - await db.execute('INSERT INTO WebTests (id, value) VALUES (?, ?)', [1, 'ok']); - - const result = await db.execute('SELECT value FROM WebTests WHERE id = ?', [1]); - - expect(result.rows[0]?.value).toEqual('ok'); - - await db.closeAsync(); - }); - - it('open() throws on web', () => { - let didThrow = false; - - try { - open({ - name: 'web-tests.sqlite', - }); - } catch (error) { - didThrow = true; - expect((error as Error).message.includes('async-only')).toEqual(true); - } - - expect(didThrow).toEqual(true); - }); - - it('executeSync() throws on web', async () => { - let db: DB | null = null; - - try { - db = await openAsync({ - name: 'web-tests.sqlite', - }); - - let didThrow = false; - - try { - db.executeSync('SELECT 1'); - } catch (_error) { - didThrow = true; - } - - expect(didThrow).toEqual(true); - } finally { - await db?.closeAsync(); - } - }); - - it('rejects SQLCipher options on web', async () => { - let didThrow = false; - - try { - await openAsync({ - name: 'web-tests-encrypted.sqlite', - encryptionKey: 'not-supported-on-web', - }); - } catch (error) { - didThrow = true; - expect((error as Error).message.includes('SQLCipher')).toEqual(true); - } - - expect(didThrow).toEqual(true); - }); +import { type DB, open, openAsync } from "@op-engineering/op-sqlite"; +import { describe, expect, it } from "@op-engineering/op-test"; +import { Platform } from "react-native"; + +describe("Web backend", () => { + if (Platform.OS !== "web") { + return; + } + + it("opens with openAsync and executes queries", async () => { + const db = await openAsync({ + name: "web-tests.sqlite", + }); + + await db.execute( + "CREATE TABLE IF NOT EXISTS WebTests (id INTEGER PRIMARY KEY, value TEXT NOT NULL)", + ); + await db.execute("DELETE FROM WebTests"); + await db.execute("INSERT INTO WebTests (id, value) VALUES (?, ?)", [ + 1, + "ok", + ]); + + const result = await db.execute( + "SELECT value FROM WebTests WHERE id = ?", + [1], + ); + + expect(result.rows[0]?.value).toEqual("ok"); + + await db.closeAsync(); + }); + + it("open() throws on web", () => { + let didThrow = false; + + try { + open({ + name: "web-tests.sqlite", + }); + } catch (error) { + didThrow = true; + expect((error as Error).message.includes("async-only")).toEqual(true); + } + + expect(didThrow).toEqual(true); + }); + + it("executeSync() throws on web", async () => { + let db: DB | null = null; + + try { + db = await openAsync({ + name: "web-tests.sqlite", + }); + + let didThrow = false; + + try { + db.executeSync("SELECT 1"); + } catch (_error) { + didThrow = true; + } + + expect(didThrow).toEqual(true); + } finally { + await db?.closeAsync(); + } + }); + + it("rejects SQLCipher options on web", async () => { + let didThrow = false; + + try { + await openAsync({ + name: "web-tests-encrypted.sqlite", + encryptionKey: "not-supported-on-web", + }); + } catch (error) { + didThrow = true; + expect((error as Error).message.includes("SQLCipher")).toEqual(true); + } + + expect(didThrow).toEqual(true); + }); }); diff --git a/package.json b/package.json index 82b22353..de8fc603 100644 --- a/package.json +++ b/package.json @@ -1,128 +1,133 @@ { - "name": "@op-engineering/op-sqlite", - "version": "0.0.0", - "description": "Fastest SQLite for React Native (with node.js support)", - "main": "./lib/module/index.js", - "types": "./lib/typescript/src/index.d.ts", - "react-native": "src/index", - "exports": { - ".": { - "source": "./src/index.ts", - "node": "./node/dist/index.js", - "types": "./lib/typescript/src/index.d.ts", - "default": "./lib/module/index.js" - }, - "./package.json": "./package.json" - }, - "files": [ - "src", - "lib", - "android", - "ios", - "cpp", - "node/dist", - "node/package.json", - "*.podspec", - "*.rb", - "react-native.config.js", - "ios/**.xcframework", - "!ios/build", - "!android/build", - "!android/gradle", - "!android/gradlew", - "!android/gradlew.bat", - "!android/local.properties", - "!**/__tests__", - "!**/__fixtures__", - "!**/__mocks__", - "!**/.*" - ], - "scripts": { - "test:node": "yarn workspace node test", - "example": "yarn workspace op_sqlite_example", - "typecheck": "tsc", - "prepare": "bob build && yarn build:node", - "build:node": "yarn workspace node build", - "pods": "cd example && yarn pods", - "clang-format-check": "clang-format -i cpp/*.cpp cpp/*.h" - }, - "keywords": [ - "react-native", - "ios", - "android", - "node", - "sqlite", - "database" - ], - "repository": "https://github.com/OP-Engineering/op-sqlite", - "author": "Oscar Franco (https://github.com/ospfranco)", - "license": "MIT", - "bugs": { - "url": "https://github.com/OP-Engineering/op-sqlite/issues" - }, - "homepage": "https://github.com/OP-Engineering/op-sqlite#readme", - "publishConfig": { - "registry": "https://registry.npmjs.org/" - }, - "devDependencies": { - "@types/better-sqlite3": "^7.6.13", - "@types/jest": "^30.0.0", - "better-sqlite3": "^12.5.0", - "clang-format": "^1.8.0", - "jest": "^29.5.0", - "react": "19.1.1", - "react-native": "0.82.1", - "react-native-builder-bob": "^0.40.15", - "typescript": "^5.9.2" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - }, - "workspaces": [ - "example", - "node" - ], - "prettier": { - "quoteProps": "consistent", - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "es5", - "useTabs": false - }, - "react-native-builder-bob": { - "source": "src", - "output": "lib", - "targets": [ - [ - "module", - { - "esm": true - } - ], - [ - "typescript", - { - "project": "tsconfig.build.json" - } - ] - ] - }, - "codegenConfig": { - "name": "OPSQLiteSpec", - "type": "modules", - "jsSrcsDir": "src", - "android": { - "javaPackageName": "com.op.sqlite" - } - }, - "create-react-native-library": { - "languages": "kotlin-objc", - "type": "turbo-module", - "version": "0.52.1" - }, - "packageManager": "yarn@4.11.0", - "dependencies": { - "@sqlite.org/sqlite-wasm": "^3.51.2-build8" - } + "name": "@op-engineering/op-sqlite", + "version": "0.0.0", + "description": "Fastest SQLite for React Native (with node.js support)", + "main": "./lib/module/index.js", + "types": "./lib/typescript/src/index.d.ts", + "react-native": "src/index", + "exports": { + ".": { + "source": "./src/index.ts", + "node": "./node/dist/index.js", + "types": "./lib/typescript/src/index.d.ts", + "default": "./lib/module/index.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "src", + "lib", + "android", + "ios", + "cpp", + "node/dist", + "node/package.json", + "*.podspec", + "*.rb", + "react-native.config.js", + "ios/**.xcframework", + "!ios/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "test:node": "yarn workspace node test", + "example": "yarn workspace op_sqlite_example", + "typecheck": "tsc", + "prepare": "bob build && yarn build:node", + "build:node": "yarn workspace node build", + "pods": "cd example && yarn pods", + "clang-format-check": "clang-format -i cpp/*.cpp cpp/*.h" + }, + "keywords": [ + "react-native", + "ios", + "android", + "node", + "sqlite", + "database" + ], + "repository": "https://github.com/OP-Engineering/op-sqlite", + "author": "Oscar Franco (https://github.com/ospfranco)", + "license": "MIT", + "bugs": { + "url": "https://github.com/OP-Engineering/op-sqlite/issues" + }, + "homepage": "https://github.com/OP-Engineering/op-sqlite#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "devDependencies": { + "@biomejs/biome": "^2.4.10", + "@sqlite.org/sqlite-wasm": "^3.51.2-build8", + "@types/better-sqlite3": "^7.6.13", + "@types/jest": "^30.0.0", + "better-sqlite3": "^12.5.0", + "clang-format": "^1.8.0", + "jest": "^29.5.0", + "react": "19.1.1", + "react-native": "0.82.1", + "react-native-builder-bob": "^0.40.15", + "typescript": "^5.9.2" + }, + "peerDependencies": { + "@sqlite.org/sqlite-wasm": "*", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@sqlite.org/sqlite-wasm": { + "optional": true + } + }, + "workspaces": [ + "example", + "node" + ], + "prettier": { + "quoteProps": "consistent", + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + [ + "module", + { + "esm": true + } + ], + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + }, + "codegenConfig": { + "name": "OPSQLiteSpec", + "type": "modules", + "jsSrcsDir": "src", + "android": { + "javaPackageName": "com.op.sqlite" + } + }, + "create-react-native-library": { + "languages": "kotlin-objc", + "type": "turbo-module", + "version": "0.52.1" + }, + "packageManager": "yarn@4.11.0" } diff --git a/src/functions.ts b/src/functions.ts index 399888ee..f1f90134 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -1,364 +1,364 @@ -import { NativeModules, Platform } from 'react-native'; +import { NativeModules, Platform } from "react-native"; import type { - _InternalDB, - _PendingTransaction, - BatchQueryResult, - DB, - DBParams, - OPSQLiteProxy, - QueryResult, - Scalar, - SQLBatchTuple, - Transaction, -} from './types'; + _InternalDB, + _PendingTransaction, + BatchQueryResult, + DB, + DBParams, + OPSQLiteProxy, + QueryResult, + Scalar, + SQLBatchTuple, + Transaction, +} from "./types"; declare global { - var __OPSQLiteProxy: object | undefined; + var __OPSQLiteProxy: object | undefined; } if (global.__OPSQLiteProxy == null) { - if (NativeModules.OPSQLite == null) { - throw new Error( - 'Base module not found. Did you do a pod install/clear the gradle cache?' - ); - } - - // Call the synchronous blocking install() function - const installed = NativeModules.OPSQLite.install(); - if (!installed) { - throw new Error( - `Failed to install op-sqlite: The native OPSQLite Module could not be installed! Looks like something went wrong when installing JSI bindings, check the native logs for more info` - ); - } - - // Check again if the constructor now exists. If not, throw an error. - if (global.__OPSQLiteProxy == null) { - throw new Error( - 'OPSqlite native object is not available. Something is wrong. Check the native logs for more information.' - ); - } + if (NativeModules.OPSQLite == null) { + throw new Error( + "Base module not found. Did you do a pod install/clear the gradle cache?", + ); + } + + // Call the synchronous blocking install() function + const installed = NativeModules.OPSQLite.install(); + if (!installed) { + throw new Error( + `Failed to install op-sqlite: The native OPSQLite Module could not be installed! Looks like something went wrong when installing JSI bindings, check the native logs for more info`, + ); + } + + // Check again if the constructor now exists. If not, throw an error. + if (global.__OPSQLiteProxy == null) { + throw new Error( + "OPSqlite native object is not available. Something is wrong. Check the native logs for more information.", + ); + } } const proxy = global.__OPSQLiteProxy; export const OPSQLite = proxy as OPSQLiteProxy; function enhanceDB(db: _InternalDB, options: DBParams): DB { - const lock = { - queue: [] as _PendingTransaction[], - inProgress: false, - }; - - const startNextTransaction = () => { - if (lock.inProgress) { - // Transaction is already in process bail out - return; - } - - if (lock.queue.length) { - lock.inProgress = true; - const tx = lock.queue.shift(); - - if (!tx) { - throw new Error('Could not get a operation on database'); - } - - setImmediate(() => { - tx.start(); - }); - } - }; - - function sanitizeArrayBuffersInArray( - params?: any[] | any[][] - ): any[] | undefined { - if (!params) { - return params; - } - - return params.map((p) => { - if (Array.isArray(p)) { - return sanitizeArrayBuffersInArray(p); - } - - if (ArrayBuffer.isView(p)) { - return p.buffer; - } - - return p; - }); - } - - // spreading the object does not work with HostObjects (db) - // We need to manually assign the fields - let enhancedDb = { - delete: db.delete, - attach: db.attach, - detach: db.detach, - loadFile: db.loadFile, - updateHook: db.updateHook, - commitHook: db.commitHook, - rollbackHook: db.rollbackHook, - loadExtension: db.loadExtension, - getDbPath: db.getDbPath, - reactiveExecute: db.reactiveExecute, - sync: db.sync, - setReservedBytes: db.setReservedBytes, - getReservedBytes: db.getReservedBytes, - close: db.close, - closeAsync: async () => { - db.close(); - }, - flushPendingReactiveQueries: db.flushPendingReactiveQueries, - executeBatch: async ( - commands: SQLBatchTuple[] - ): Promise => { - // Do normal for loop and replace in place for performance - for (let i = 0; i < commands.length; i++) { - // [1] is the params arg - if (commands[i]![1]) { - commands[i]![1] = sanitizeArrayBuffersInArray(commands[i]![1]) as any; - } - } - - async function run() { - try { - enhancedDb.executeSync('BEGIN TRANSACTION;'); - - let res = await db.executeBatch(commands as any[]); - - enhancedDb.executeSync('COMMIT;'); - - await db.flushPendingReactiveQueries(); - - return res; - } catch (executionError) { - try { - enhancedDb.executeSync('ROLLBACK;'); - } catch (rollbackError) { - throw rollbackError; - } - - throw executionError; - } finally { - lock.inProgress = false; - startNextTransaction(); - } - } - - return await new Promise((resolve, reject) => { - const tx: _PendingTransaction = { - start: () => { - run().then(resolve).catch(reject); - }, - }; - - lock.queue.push(tx); - startNextTransaction(); - }); - }, - executeWithHostObjects: async ( - query: string, - params?: Scalar[] - ): Promise => { - const sanitizedParams = sanitizeArrayBuffersInArray(params); - - return sanitizedParams - ? await db.executeWithHostObjects(query, sanitizedParams as Scalar[]) - : await db.executeWithHostObjects(query); - }, - executeRaw: async (query: string, params?: Scalar[]) => { - const sanitizedParams = sanitizeArrayBuffersInArray(params); - - return db.executeRaw(query, sanitizedParams as Scalar[]); - }, - executeRawSync: (query: string, params?: Scalar[]) => { - const sanitizedParams = sanitizeArrayBuffersInArray(params); - return db.executeRawSync(query, sanitizedParams as Scalar[]); - }, - // Wrapper for executeRaw, drizzleORM uses this function - // at some point I changed the API but they did not pin their dependency to a specific version - // so re-inserting this so it starts working again - executeRawAsync: async (query: string, params?: Scalar[]) => { - const sanitizedParams = sanitizeArrayBuffersInArray(params); - - return db.executeRaw(query, sanitizedParams as Scalar[]); - }, - executeSync: (query: string, params?: Scalar[]): QueryResult => { - let res = params - ? db.executeSync(query, sanitizeArrayBuffersInArray(params) as Scalar[]) - : db.executeSync(query); - - if (!res.rows) { - let rows: Record[] = []; - for (let i = 0; i < (res.rawRows?.length ?? 0); i++) { - let row: Record = {}; - let rawRow = res.rawRows![i]!; - for (let j = 0; j < res.columnNames!.length; j++) { - let columnName = res.columnNames![j]!; - let value = rawRow[j]!; - - row[columnName] = value; - } - rows.push(row); - } - - delete res.rawRows; - - res = { - ...res, - rows, - }; - } - - return res; - }, - executeAsync: async ( - query: string, - params?: Scalar[] | undefined - ): Promise => { - return db.execute(query, params); - }, - execute: async ( - query: string, - params?: Scalar[] | undefined - ): Promise => { - let res = params - ? await db.execute( - query, - sanitizeArrayBuffersInArray(params) as Scalar[] - ) - : await db.execute(query); - - if (!res.rows) { - let rows: Record[] = []; - for (let i = 0; i < (res.rawRows?.length ?? 0); i++) { - let row: Record = {}; - let rawRow = res.rawRows![i]!; - for (let j = 0; j < res.columnNames!.length; j++) { - let columnName = res.columnNames![j]!; - let value = rawRow[j]!; - - row[columnName] = value; - } - rows.push(row); - } - - delete res.rawRows; - - res = { - ...res, - rows, - }; - } - - return res; - }, - prepareStatement: (query: string) => { - const stmt = db.prepareStatement(query); - - return { - bindSync: (params: Scalar[]) => { - const sanitizedParams = sanitizeArrayBuffersInArray(params); - - stmt.bindSync(sanitizedParams!); - }, - bind: async (params: Scalar[]) => { - const sanitizedParams = sanitizeArrayBuffersInArray(params); - - await stmt.bind(sanitizedParams!); - }, - execute: stmt.execute, - }; - }, - transaction: async ( - fn: (tx: Transaction) => Promise - ): Promise => { - let isFinalized = false; - - const execute = async (query: string, params?: Scalar[]) => { - if (isFinalized) { - throw Error( - `OP-Sqlite Error: Database: ${ - options.name || options.url - }. Cannot execute query on finalized transaction` - ); - } - return await enhancedDb.execute(query, params); - }; - - const commit = async (): Promise => { - if (isFinalized) { - throw Error( - `OP-Sqlite Error: Database: ${ - options.name || options.url - }. Cannot execute query on finalized transaction` - ); - } - const result = enhancedDb.executeSync('COMMIT;'); - - await db.flushPendingReactiveQueries(); - - isFinalized = true; - return result; - }; - - const rollback = (): QueryResult => { - if (isFinalized) { - throw Error( - `OP-Sqlite Error: Database: ${ - options.name || options.url - }. Cannot execute query on finalized transaction` - ); - } - const result = enhancedDb.executeSync('ROLLBACK;'); - isFinalized = true; - return result; - }; - - async function run() { - try { - enhancedDb.executeSync('BEGIN TRANSACTION;'); - - await fn({ - commit, - execute, - rollback, - }); - - if (!isFinalized) { - commit(); - } - } catch (executionError) { - if (!isFinalized) { - try { - rollback(); - } catch (rollbackError) { - throw rollbackError; - } - } - - throw executionError; - } finally { - lock.inProgress = false; - isFinalized = false; - startNextTransaction(); - } - } - - return await new Promise((resolve, reject) => { - const tx: _PendingTransaction = { - start: () => { - run().then(resolve).catch(reject); - }, - }; - - lock.queue.push(tx); - startNextTransaction(); - }); - }, - }; - - return enhancedDb; + const lock = { + queue: [] as _PendingTransaction[], + inProgress: false, + }; + + const startNextTransaction = () => { + if (lock.inProgress) { + // Transaction is already in process bail out + return; + } + + if (lock.queue.length) { + lock.inProgress = true; + const tx = lock.queue.shift(); + + if (!tx) { + throw new Error("Could not get a operation on database"); + } + + setImmediate(() => { + tx.start(); + }); + } + }; + + function sanitizeArrayBuffersInArray( + params?: any[] | any[][], + ): any[] | undefined { + if (!params) { + return params; + } + + return params.map((p) => { + if (Array.isArray(p)) { + return sanitizeArrayBuffersInArray(p); + } + + if (ArrayBuffer.isView(p)) { + return p.buffer; + } + + return p; + }); + } + + // spreading the object does not work with HostObjects (db) + // We need to manually assign the fields + const enhancedDb = { + delete: db.delete, + attach: db.attach, + detach: db.detach, + loadFile: db.loadFile, + updateHook: db.updateHook, + commitHook: db.commitHook, + rollbackHook: db.rollbackHook, + loadExtension: db.loadExtension, + getDbPath: db.getDbPath, + reactiveExecute: db.reactiveExecute, + sync: db.sync, + setReservedBytes: db.setReservedBytes, + getReservedBytes: db.getReservedBytes, + close: db.close, + closeAsync: async () => { + db.close(); + }, + flushPendingReactiveQueries: db.flushPendingReactiveQueries, + executeBatch: async ( + commands: SQLBatchTuple[], + ): Promise => { + // Do normal for loop and replace in place for performance + for (let i = 0; i < commands.length; i++) { + // [1] is the params arg + if (commands[i]![1]) { + commands[i]![1] = sanitizeArrayBuffersInArray(commands[i]![1]) as any; + } + } + + async function run() { + try { + enhancedDb.executeSync("BEGIN TRANSACTION;"); + + const res = await db.executeBatch(commands as any[]); + + enhancedDb.executeSync("COMMIT;"); + + await db.flushPendingReactiveQueries(); + + return res; + } catch (executionError) { + try { + enhancedDb.executeSync("ROLLBACK;"); + } catch (rollbackError) { + throw rollbackError; + } + + throw executionError; + } finally { + lock.inProgress = false; + startNextTransaction(); + } + } + + return await new Promise((resolve, reject) => { + const tx: _PendingTransaction = { + start: () => { + run().then(resolve).catch(reject); + }, + }; + + lock.queue.push(tx); + startNextTransaction(); + }); + }, + executeWithHostObjects: async ( + query: string, + params?: Scalar[], + ): Promise => { + const sanitizedParams = sanitizeArrayBuffersInArray(params); + + return sanitizedParams + ? await db.executeWithHostObjects(query, sanitizedParams as Scalar[]) + : await db.executeWithHostObjects(query); + }, + executeRaw: async (query: string, params?: Scalar[]) => { + const sanitizedParams = sanitizeArrayBuffersInArray(params); + + return db.executeRaw(query, sanitizedParams as Scalar[]); + }, + executeRawSync: (query: string, params?: Scalar[]) => { + const sanitizedParams = sanitizeArrayBuffersInArray(params); + return db.executeRawSync(query, sanitizedParams as Scalar[]); + }, + // Wrapper for executeRaw, drizzleORM uses this function + // at some point I changed the API but they did not pin their dependency to a specific version + // so re-inserting this so it starts working again + executeRawAsync: async (query: string, params?: Scalar[]) => { + const sanitizedParams = sanitizeArrayBuffersInArray(params); + + return db.executeRaw(query, sanitizedParams as Scalar[]); + }, + executeSync: (query: string, params?: Scalar[]): QueryResult => { + let res = params + ? db.executeSync(query, sanitizeArrayBuffersInArray(params) as Scalar[]) + : db.executeSync(query); + + if (!res.rows) { + const rows: Record[] = []; + for (let i = 0; i < (res.rawRows?.length ?? 0); i++) { + const row: Record = {}; + const rawRow = res.rawRows![i]!; + for (let j = 0; j < res.columnNames!.length; j++) { + const columnName = res.columnNames![j]!; + const value = rawRow[j]!; + + row[columnName] = value; + } + rows.push(row); + } + + delete res.rawRows; + + res = { + ...res, + rows, + }; + } + + return res; + }, + executeAsync: async ( + query: string, + params?: Scalar[] | undefined, + ): Promise => { + return db.execute(query, params); + }, + execute: async ( + query: string, + params?: Scalar[] | undefined, + ): Promise => { + let res = params + ? await db.execute( + query, + sanitizeArrayBuffersInArray(params) as Scalar[], + ) + : await db.execute(query); + + if (!res.rows) { + const rows: Record[] = []; + for (let i = 0; i < (res.rawRows?.length ?? 0); i++) { + const row: Record = {}; + const rawRow = res.rawRows![i]!; + for (let j = 0; j < res.columnNames!.length; j++) { + const columnName = res.columnNames![j]!; + const value = rawRow[j]!; + + row[columnName] = value; + } + rows.push(row); + } + + delete res.rawRows; + + res = { + ...res, + rows, + }; + } + + return res; + }, + prepareStatement: (query: string) => { + const stmt = db.prepareStatement(query); + + return { + bindSync: (params: Scalar[]) => { + const sanitizedParams = sanitizeArrayBuffersInArray(params); + + stmt.bindSync(sanitizedParams!); + }, + bind: async (params: Scalar[]) => { + const sanitizedParams = sanitizeArrayBuffersInArray(params); + + await stmt.bind(sanitizedParams!); + }, + execute: stmt.execute, + }; + }, + transaction: async ( + fn: (tx: Transaction) => Promise, + ): Promise => { + let isFinalized = false; + + const execute = async (query: string, params?: Scalar[]) => { + if (isFinalized) { + throw Error( + `OP-Sqlite Error: Database: ${ + options.name || options.url + }. Cannot execute query on finalized transaction`, + ); + } + return await enhancedDb.execute(query, params); + }; + + const commit = async (): Promise => { + if (isFinalized) { + throw Error( + `OP-Sqlite Error: Database: ${ + options.name || options.url + }. Cannot execute query on finalized transaction`, + ); + } + const result = enhancedDb.executeSync("COMMIT;"); + + await db.flushPendingReactiveQueries(); + + isFinalized = true; + return result; + }; + + const rollback = (): QueryResult => { + if (isFinalized) { + throw Error( + `OP-Sqlite Error: Database: ${ + options.name || options.url + }. Cannot execute query on finalized transaction`, + ); + } + const result = enhancedDb.executeSync("ROLLBACK;"); + isFinalized = true; + return result; + }; + + async function run() { + try { + enhancedDb.executeSync("BEGIN TRANSACTION;"); + + await fn({ + commit, + execute, + rollback, + }); + + if (!isFinalized) { + commit(); + } + } catch (executionError) { + if (!isFinalized) { + try { + rollback(); + } catch (rollbackError) { + throw rollbackError; + } + } + + throw executionError; + } finally { + lock.inProgress = false; + isFinalized = false; + startNextTransaction(); + } + } + + return await new Promise((resolve, reject) => { + const tx: _PendingTransaction = { + start: () => { + run().then(resolve).catch(reject); + }, + }; + + lock.queue.push(tx); + startNextTransaction(); + }); + }, + }; + + return enhancedDb; } /** @@ -366,23 +366,23 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB { * libsql needs to be enabled on your package.json */ export const openSync = (params: { - url: string; - authToken: string; - name: string; - location?: string; - libsqlSyncInterval?: number; - libsqlOffline?: boolean; - encryptionKey?: string; - remoteEncryptionKey?: string; + url: string; + authToken: string; + name: string; + location?: string; + libsqlSyncInterval?: number; + libsqlOffline?: boolean; + encryptionKey?: string; + remoteEncryptionKey?: string; }): DB => { - if (!isLibsql()) { - throw new Error('This function is only available for libsql'); - } + if (!isLibsql()) { + throw new Error("This function is only available for libsql"); + } - const db = OPSQLite.openSync(params); - const enhancedDb = enhanceDB(db, params); + const db = OPSQLite.openSync(params); + const enhancedDb = enhanceDB(db, params); - return enhancedDb; + return enhancedDb; }; /** @@ -390,14 +390,14 @@ export const openSync = (params: { * libsql needs to be enabled on your package.json */ export const openRemote = (params: { url: string; authToken: string }): DB => { - if (!isLibsql()) { - throw new Error('This function is only available for libsql'); - } + if (!isLibsql()) { + throw new Error("This function is only available for libsql"); + } - const db = OPSQLite.openRemote(params); - const enhancedDb = enhanceDB(db, params); + const db = OPSQLite.openRemote(params); + const enhancedDb = enhanceDB(db, params); - return enhancedDb; + return enhancedDb; }; /** @@ -405,21 +405,21 @@ export const openRemote = (params: { url: string; authToken: string }): DB => { * If you want libsql remote or sync connections, use openSync or openRemote */ export const open = (params: { - name: string; - location?: string; - encryptionKey?: string; + name: string; + location?: string; + encryptionKey?: string; }): DB => { - if (params.location?.startsWith('file://')) { - console.warn( - "[op-sqlite] You are passing a path with 'file://' prefix, it's automatically removed" - ); - params.location = params.location.substring(7); - } + if (params.location?.startsWith("file://")) { + console.warn( + "[op-sqlite] You are passing a path with 'file://' prefix, it's automatically removed", + ); + params.location = params.location.substring(7); + } - const db = OPSQLite.open(params); - const enhancedDb = enhanceDB(db, params); + const db = OPSQLite.open(params); + const enhancedDb = enhanceDB(db, params); - return enhancedDb; + return enhancedDb; }; /** @@ -427,11 +427,11 @@ export const open = (params: { * Useful for cross-platform code that also targets web where openAsync() is required. */ export const openAsync = async (params: { - name: string; - location?: string; - encryptionKey?: string; + name: string; + location?: string; + encryptionKey?: string; }): Promise => { - return open(params); + return open(params); }; /** @@ -442,11 +442,11 @@ export const openAsync = async (params: { * @returns promise, rejects if failed to move the database, resolves if the operation was successful */ export const moveAssetsDatabase = async (args: { - filename: string; - path?: string; - overwrite?: boolean; + filename: string; + path?: string; + overwrite?: boolean; }): Promise => { - return NativeModules.OPSQLite.moveAssetsDatabase(args); + return NativeModules.OPSQLite.moveAssetsDatabase(args); }; /** @@ -458,23 +458,23 @@ export const moveAssetsDatabase = async (args: { * @returns */ export const getDylibPath = (bundle: string, name: string): string => { - return NativeModules.OPSQLite.getDylibPath(bundle, name); + return NativeModules.OPSQLite.getDylibPath(bundle, name); }; export const isSQLCipher = (): boolean => { - return OPSQLite.isSQLCipher(); + return OPSQLite.isSQLCipher(); }; export const isLibsql = (): boolean => { - return OPSQLite.isLibsql(); + return OPSQLite.isLibsql(); }; export const isIOSEmbedded = (): boolean => { - if (Platform.OS !== 'ios') { - return false; - } + if (Platform.OS !== "ios") { + return false; + } - return OPSQLite.isIOSEmbedded(); + return OPSQLite.isIOSEmbedded(); }; /** diff --git a/src/functions.web.ts b/src/functions.web.ts index e9d0a07a..fe2c1b32 100644 --- a/src/functions.web.ts +++ b/src/functions.web.ts @@ -1,419 +1,441 @@ import type { - _InternalDB, - _PendingTransaction, - BatchQueryResult, - DB, - DBParams, - FileLoadResult, - OPSQLiteProxy, - PreparedStatement, - QueryResult, - Scalar, - SQLBatchTuple, - Transaction, -} from './types'; - -type WorkerPromiser = (type: string, args?: Record) => Promise; + _InternalDB, + _PendingTransaction, + BatchQueryResult, + DB, + DBParams, + FileLoadResult, + OPSQLiteProxy, + PreparedStatement, + QueryResult, + Scalar, + SQLBatchTuple, + Transaction, +} from "./types"; + +type WorkerPromiser = ( + type: string, + args?: Record, +) => Promise; const WEB_ONLY_SYNC_ERROR = - '[op-sqlite] Web backend is async-only. Use openAsync() and async methods like execute().'; + "[op-sqlite] Web backend is async-only. Use openAsync() and async methods like execute()."; function throwSyncApiError(method: string): never { - throw new Error(`${WEB_ONLY_SYNC_ERROR} Called sync method: ${method}().`); + throw new Error(`${WEB_ONLY_SYNC_ERROR} Called sync method: ${method}().`); } function toNumber(value: unknown): number | undefined { - if (value == null) { - return undefined; - } + if (value == null) { + return undefined; + } - if (typeof value === 'bigint') { - const asNumber = Number(value); - return Number.isFinite(asNumber) ? asNumber : undefined; - } + if (typeof value === "bigint") { + const asNumber = Number(value); + return Number.isFinite(asNumber) ? asNumber : undefined; + } - if (typeof value === 'number') { - return Number.isFinite(value) ? value : undefined; - } + if (typeof value === "number") { + return Number.isFinite(value) ? value : undefined; + } - return undefined; + return undefined; } function ensureSingleStatement(sql: string): void { - // Web worker executes the full SQL string while native executes only the first prepared statement. - // We warn here so callers can keep behavior consistent across platforms when needed. - if (sql.includes(';')) { - const trimmed = sql.trim(); - if (!trimmed.endsWith(';') || trimmed.slice(0, -1).includes(';')) { - console.warn( - '[op-sqlite] Web execute() runs full SQL strings. Avoid multi-statement SQL for parity with native first-statement behavior.' - ); - } - } + // Web worker executes the full SQL string while native executes only the first prepared statement. + // We warn here so callers can keep behavior consistent across platforms when needed. + if (sql.includes(";")) { + const trimmed = sql.trim(); + if (!trimmed.endsWith(";") || trimmed.slice(0, -1).includes(";")) { + console.warn( + "[op-sqlite] Web execute() runs full SQL strings. Avoid multi-statement SQL for parity with native first-statement behavior.", + ); + } + } } let promiserPromise: Promise | null = null; async function getPromiser(): Promise { - if (!promiserPromise) { - promiserPromise = (async () => { - const mod = await import('@sqlite.org/sqlite-wasm'); - const makePromiser = mod.sqlite3Worker1Promiser as any; + if (!promiserPromise) { + promiserPromise = (async () => { + let mod: any; - const maybePromiser = makePromiser(); + try { + mod = await import("@sqlite.org/sqlite-wasm"); + } catch (error) { + throw new Error( + `[op-sqlite] Web support requires optional dependency @sqlite.org/sqlite-wasm. Install it in your app: npm i @sqlite.org/sqlite-wasm (or yarn add @sqlite.org/sqlite-wasm). Original error: ${(error as Error).message}`, + ); + } - if (typeof maybePromiser === 'function') { - return maybePromiser as WorkerPromiser; - } + const makePromiser = mod.sqlite3Worker1Promiser as any; - return (await maybePromiser) as WorkerPromiser; - })(); - } + const maybePromiser = makePromiser(); - return promiserPromise; + if (typeof maybePromiser === "function") { + return maybePromiser as WorkerPromiser; + } + + return (await maybePromiser) as WorkerPromiser; + })(); + } + + return promiserPromise; } async function ensureOpfs(promiser: WorkerPromiser): Promise { - const config = await promiser('config-get', {}); - const vfsList = config?.result?.vfsList; - - if (!Array.isArray(vfsList) || !vfsList.includes('opfs')) { - throw new Error( - '[op-sqlite] OPFS is required on web for persistence. Ensure COOP/COEP headers are set and OPFS is available in this browser.' - ); - } + const config = await promiser("config-get", {}); + const vfsList = config?.result?.vfsList; + + if (!Array.isArray(vfsList) || !vfsList.includes("opfs")) { + throw new Error( + "[op-sqlite] OPFS is required on web for persistence. Ensure COOP/COEP headers are set and OPFS is available in this browser.", + ); + } } async function executeWorker( - promiser: WorkerPromiser, - dbId: string, - query: string, - params?: Scalar[] + promiser: WorkerPromiser, + dbId: string, + query: string, + params?: Scalar[], ): Promise { - ensureSingleStatement(query); - - const response = await promiser('exec', { - dbId, - sql: query, - bind: params, - rowMode: 'object', - resultRows: [], - columnNames: [], - returnValue: 'resultRows', - }); - - const result = response?.result; - const rows = Array.isArray(result?.resultRows) - ? (result.resultRows as Array>) - : Array.isArray(result) - ? (result as Array>) - : []; - const columnNames = Array.isArray(result?.columnNames) - ? (result.columnNames as string[]) - : rows.length > 0 - ? Object.keys(rows[0] ?? {}) - : []; - - const rowsAffected = toNumber(result?.changeCount) ?? 0; - const insertId = toNumber(result?.lastInsertRowId); - - return { - rowsAffected, - insertId, - rows, - columnNames, - }; + ensureSingleStatement(query); + + const response = await promiser("exec", { + dbId, + sql: query, + bind: params, + rowMode: "object", + resultRows: [], + columnNames: [], + returnValue: "resultRows", + }); + + const result = response?.result; + const rows = Array.isArray(result?.resultRows) + ? (result.resultRows as Array>) + : Array.isArray(result) + ? (result as Array>) + : []; + const columnNames = Array.isArray(result?.columnNames) + ? (result.columnNames as string[]) + : rows.length > 0 + ? Object.keys(rows[0] ?? {}) + : []; + + const rowsAffected = toNumber(result?.changeCount) ?? 0; + const insertId = toNumber(result?.lastInsertRowId); + + return { + rowsAffected, + insertId, + rows, + columnNames, + }; } function enhanceWebDb( - db: _InternalDB, - options: { name?: string; location?: string } + db: _InternalDB, + options: { name?: string; location?: string }, ): DB { - const lock = { - queue: [] as _PendingTransaction[], - inProgress: false, - }; - - const startNextTransaction = () => { - if (lock.inProgress || lock.queue.length === 0) { - return; - } - - lock.inProgress = true; - const tx = lock.queue.shift(); - if (!tx) { - throw new Error('Could not get an operation on database'); - } - - setTimeout(() => { - tx.start(); - }, 0); - }; - - const withTransactionLock = async (work: () => Promise): Promise => { - return new Promise((resolve, reject) => { - const tx: _PendingTransaction = { - start: () => { - work() - .then(resolve) - .catch(reject) - .finally(() => { - lock.inProgress = false; - startNextTransaction(); - }); - }, - }; - - lock.queue.push(tx); - startNextTransaction(); - }); - }; - - const unsupported = (method: string) => () => throwSyncApiError(method); - - const enhancedDb: DB = { - close: unsupported('close'), - closeAsync: async () => { - await db.closeAsync?.(); - }, - delete: unsupported('delete'), - attach: unsupported('attach'), - detach: unsupported('detach'), - transaction: async (fn: (tx: Transaction) => Promise): Promise => { - return withTransactionLock(async () => { - let finalized = false; - - const commit = async (): Promise => { - if (finalized) { - throw new Error( - `OP-Sqlite Error: Database: ${options.name}. Cannot execute query on finalized transaction` - ); - } - - const res = await enhancedDb.execute('COMMIT;'); - finalized = true; - return res; - }; - - const rollback = (): QueryResult => { - throwSyncApiError('rollback'); - }; - - const execute = async (query: string, params?: Scalar[]) => { - if (finalized) { - throw new Error( - `OP-Sqlite Error: Database: ${options.name}. Cannot execute query on finalized transaction` - ); - } - - return enhancedDb.execute(query, params); - }; - - await enhancedDb.execute('BEGIN TRANSACTION;'); - - try { - await fn({ - execute, - commit, - rollback, - }); - - if (!finalized) { - await commit(); - } - } catch (error) { - if (!finalized) { - await enhancedDb.execute('ROLLBACK;'); - } - - throw error; - } - }); - }, - executeSync: unsupported('executeSync'), - execute: db.execute, - executeWithHostObjects: db.execute, - executeBatch: async (commands: SQLBatchTuple[]): Promise => { - await withTransactionLock(async () => { - await db.execute('BEGIN TRANSACTION;'); - - try { - for (const command of commands) { - const [sql, bind] = command; - - if (!bind) { - await db.execute(sql); - continue; - } - - if (Array.isArray(bind[0])) { - for (const rowBind of bind as Scalar[][]) { - await db.execute(sql, rowBind); - } - } else { - await db.execute(sql, bind as Scalar[]); - } - } - - await db.execute('COMMIT;'); - } catch (error) { - await db.execute('ROLLBACK;'); - throw error; - } - }); - - return { - rowsAffected: 0, - }; - }, - loadFile: async (_location: string): Promise => { - throw new Error('[op-sqlite] loadFile() is not supported on web.'); - }, - updateHook: () => { - throw new Error('[op-sqlite] updateHook() is not supported on web.'); - }, - commitHook: () => { - throw new Error('[op-sqlite] commitHook() is not supported on web.'); - }, - rollbackHook: () => { - throw new Error('[op-sqlite] rollbackHook() is not supported on web.'); - }, - prepareStatement: (query: string): PreparedStatement => { - let currentParams: Scalar[] = []; - - return { - bind: async (params: Scalar[]) => { - currentParams = params; - }, - bindSync: unsupported('bindSync'), - execute: async () => { - return db.execute(query, currentParams); - }, - }; - }, - loadExtension: unsupported('loadExtension'), - executeRaw: db.executeRaw, - executeRawSync: unsupported('executeRawSync'), - getDbPath: unsupported('getDbPath'), - reactiveExecute: unsupported('reactiveExecute'), - sync: unsupported('sync'), - setReservedBytes: unsupported('setReservedBytes'), - getReservedBytes: unsupported('getReservedBytes'), - flushPendingReactiveQueries: async () => {}, - }; - - return enhancedDb; + const lock = { + queue: [] as _PendingTransaction[], + inProgress: false, + }; + + const startNextTransaction = () => { + if (lock.inProgress || lock.queue.length === 0) { + return; + } + + lock.inProgress = true; + const tx = lock.queue.shift(); + if (!tx) { + throw new Error("Could not get an operation on database"); + } + + setTimeout(() => { + tx.start(); + }, 0); + }; + + const withTransactionLock = async (work: () => Promise): Promise => { + return new Promise((resolve, reject) => { + const tx: _PendingTransaction = { + start: () => { + work() + .then(resolve) + .catch(reject) + .finally(() => { + lock.inProgress = false; + startNextTransaction(); + }); + }, + }; + + lock.queue.push(tx); + startNextTransaction(); + }); + }; + + const unsupported = (method: string) => () => throwSyncApiError(method); + + const enhancedDb: DB = { + close: unsupported("close"), + closeAsync: async () => { + await db.closeAsync?.(); + }, + delete: unsupported("delete"), + attach: unsupported("attach"), + detach: unsupported("detach"), + transaction: async ( + fn: (tx: Transaction) => Promise, + ): Promise => { + return withTransactionLock(async () => { + let finalized = false; + + const commit = async (): Promise => { + if (finalized) { + throw new Error( + `OP-Sqlite Error: Database: ${options.name}. Cannot execute query on finalized transaction`, + ); + } + + const res = await enhancedDb.execute("COMMIT;"); + finalized = true; + return res; + }; + + const rollback = (): QueryResult => { + throwSyncApiError("rollback"); + }; + + const execute = async (query: string, params?: Scalar[]) => { + if (finalized) { + throw new Error( + `OP-Sqlite Error: Database: ${options.name}. Cannot execute query on finalized transaction`, + ); + } + + return enhancedDb.execute(query, params); + }; + + await enhancedDb.execute("BEGIN TRANSACTION;"); + + try { + await fn({ + execute, + commit, + rollback, + }); + + if (!finalized) { + await commit(); + } + } catch (error) { + if (!finalized) { + await enhancedDb.execute("ROLLBACK;"); + } + + throw error; + } + }); + }, + executeSync: unsupported("executeSync"), + execute: db.execute, + executeWithHostObjects: db.execute, + executeBatch: async ( + commands: SQLBatchTuple[], + ): Promise => { + await withTransactionLock(async () => { + await db.execute("BEGIN TRANSACTION;"); + + try { + for (const command of commands) { + const [sql, bind] = command; + + if (!bind) { + await db.execute(sql); + continue; + } + + if (Array.isArray(bind[0])) { + for (const rowBind of bind as Scalar[][]) { + await db.execute(sql, rowBind); + } + } else { + await db.execute(sql, bind as Scalar[]); + } + } + + await db.execute("COMMIT;"); + } catch (error) { + await db.execute("ROLLBACK;"); + throw error; + } + }); + + return { + rowsAffected: 0, + }; + }, + loadFile: async (_location: string): Promise => { + throw new Error("[op-sqlite] loadFile() is not supported on web."); + }, + updateHook: () => { + throw new Error("[op-sqlite] updateHook() is not supported on web."); + }, + commitHook: () => { + throw new Error("[op-sqlite] commitHook() is not supported on web."); + }, + rollbackHook: () => { + throw new Error("[op-sqlite] rollbackHook() is not supported on web."); + }, + prepareStatement: (query: string): PreparedStatement => { + let currentParams: Scalar[] = []; + + return { + bind: async (params: Scalar[]) => { + currentParams = params; + }, + bindSync: unsupported("bindSync"), + execute: async () => { + return db.execute(query, currentParams); + }, + }; + }, + loadExtension: unsupported("loadExtension"), + executeRaw: db.executeRaw, + executeRawSync: unsupported("executeRawSync"), + getDbPath: unsupported("getDbPath"), + reactiveExecute: unsupported("reactiveExecute"), + sync: unsupported("sync"), + setReservedBytes: unsupported("setReservedBytes"), + getReservedBytes: unsupported("getReservedBytes"), + flushPendingReactiveQueries: async () => {}, + }; + + return enhancedDb; } async function createWebDb(params: { - name: string; - location?: string; - encryptionKey?: string; + name: string; + location?: string; + encryptionKey?: string; }): Promise<_InternalDB> { - if (params.encryptionKey) { - throw new Error('[op-sqlite] SQLCipher is not supported on web.'); - } - - const promiser = await getPromiser(); - await ensureOpfs(promiser); - - const filename = `file:${params.name}?vfs=opfs`; - const opened = await promiser('open', { - filename, - }); - - const dbId = opened?.dbId || opened?.result?.dbId; - if (!dbId || typeof dbId !== 'string') { - throw new Error('[op-sqlite] Failed to open web sqlite database.'); - } - - return { - close: () => { - throwSyncApiError('close'); - }, - closeAsync: async () => { - await promiser('close', { - dbId, - }); - }, - delete: () => { - throwSyncApiError('delete'); - }, - attach: () => { - throw new Error('[op-sqlite] attach() is not supported on web.'); - }, - detach: () => { - throw new Error('[op-sqlite] detach() is not supported on web.'); - }, - transaction: async () => { - throw new Error('[op-sqlite] transaction() must be called on an opened DB object.'); - }, - executeSync: () => { - throwSyncApiError('executeSync'); - }, - execute: async (query: string, bind?: Scalar[]) => { - return executeWorker(promiser, dbId, query, bind); - }, - executeWithHostObjects: async (query: string, bind?: Scalar[]) => { - return executeWorker(promiser, dbId, query, bind); - }, - executeBatch: async (_commands: SQLBatchTuple[]) => { - throw new Error('[op-sqlite] executeBatch() must be called on an opened DB object.'); - }, - loadFile: async (_location: string) => { - throw new Error('[op-sqlite] loadFile() is not supported on web.'); - }, - updateHook: () => { - throw new Error('[op-sqlite] updateHook() is not supported on web.'); - }, - commitHook: () => { - throw new Error('[op-sqlite] commitHook() is not supported on web.'); - }, - rollbackHook: () => { - throw new Error('[op-sqlite] rollbackHook() is not supported on web.'); - }, - prepareStatement: (_query: string) => { - throw new Error('[op-sqlite] prepareStatement() must be called on an opened DB object.'); - }, - loadExtension: () => { - throw new Error('[op-sqlite] loadExtension() is not supported on web.'); - }, - executeRaw: async (query: string, bind?: Scalar[]) => { - ensureSingleStatement(query); - - const response = await promiser('exec', { - dbId, - sql: query, - bind, - rowMode: 'array', - resultRows: [], - returnValue: 'resultRows', - }); - - const result = response?.result; - const rows = result?.resultRows ?? result; - return Array.isArray(rows) ? rows : []; - }, - executeRawSync: () => { - throwSyncApiError('executeRawSync'); - }, - getDbPath: () => { - throwSyncApiError('getDbPath'); - }, - reactiveExecute: () => { - throw new Error('[op-sqlite] reactiveExecute() is not supported on web.'); - }, - sync: () => { - throwSyncApiError('sync'); - }, - setReservedBytes: () => { - throwSyncApiError('setReservedBytes'); - }, - getReservedBytes: () => { - throwSyncApiError('getReservedBytes'); - }, - flushPendingReactiveQueries: async () => {}, - }; + if (params.encryptionKey) { + throw new Error("[op-sqlite] SQLCipher is not supported on web."); + } + + const promiser = await getPromiser(); + await ensureOpfs(promiser); + + const filename = `file:${params.name}?vfs=opfs`; + const opened = await promiser("open", { + filename, + }); + + const dbId = opened?.dbId || opened?.result?.dbId; + if (!dbId || typeof dbId !== "string") { + throw new Error("[op-sqlite] Failed to open web sqlite database."); + } + + return { + close: () => { + throwSyncApiError("close"); + }, + closeAsync: async () => { + await promiser("close", { + dbId, + }); + }, + delete: () => { + throwSyncApiError("delete"); + }, + attach: () => { + throw new Error("[op-sqlite] attach() is not supported on web."); + }, + detach: () => { + throw new Error("[op-sqlite] detach() is not supported on web."); + }, + transaction: async () => { + throw new Error( + "[op-sqlite] transaction() must be called on an opened DB object.", + ); + }, + executeSync: () => { + throwSyncApiError("executeSync"); + }, + execute: async (query: string, bind?: Scalar[]) => { + return executeWorker(promiser, dbId, query, bind); + }, + executeWithHostObjects: async (query: string, bind?: Scalar[]) => { + return executeWorker(promiser, dbId, query, bind); + }, + executeBatch: async (_commands: SQLBatchTuple[]) => { + throw new Error( + "[op-sqlite] executeBatch() must be called on an opened DB object.", + ); + }, + loadFile: async (_location: string) => { + throw new Error("[op-sqlite] loadFile() is not supported on web."); + }, + updateHook: () => { + throw new Error("[op-sqlite] updateHook() is not supported on web."); + }, + commitHook: () => { + throw new Error("[op-sqlite] commitHook() is not supported on web."); + }, + rollbackHook: () => { + throw new Error("[op-sqlite] rollbackHook() is not supported on web."); + }, + prepareStatement: (_query: string) => { + throw new Error( + "[op-sqlite] prepareStatement() must be called on an opened DB object.", + ); + }, + loadExtension: () => { + throw new Error("[op-sqlite] loadExtension() is not supported on web."); + }, + executeRaw: async (query: string, bind?: Scalar[]) => { + ensureSingleStatement(query); + + const response = await promiser("exec", { + dbId, + sql: query, + bind, + rowMode: "array", + resultRows: [], + returnValue: "resultRows", + }); + + const result = response?.result; + const rows = result?.resultRows ?? result; + return Array.isArray(rows) ? rows : []; + }, + executeRawSync: () => { + throwSyncApiError("executeRawSync"); + }, + getDbPath: () => { + throwSyncApiError("getDbPath"); + }, + reactiveExecute: () => { + throw new Error("[op-sqlite] reactiveExecute() is not supported on web."); + }, + sync: () => { + throwSyncApiError("sync"); + }, + setReservedBytes: () => { + throwSyncApiError("setReservedBytes"); + }, + getReservedBytes: () => { + throwSyncApiError("getReservedBytes"); + }, + flushPendingReactiveQueries: async () => {}, + }; } /** @@ -421,64 +443,61 @@ async function createWebDb(params: { * Web is async-only: use openAsync() and async methods like execute(). */ export const openAsync = async (params: { - name: string; - location?: string; - encryptionKey?: string; + name: string; + location?: string; + encryptionKey?: string; }): Promise => { - const db = await createWebDb(params); - return enhanceWebDb(db, params); + const db = await createWebDb(params); + return enhanceWebDb(db, params); }; export const open = (_params: { - name: string; - location?: string; - encryptionKey?: string; + name: string; + location?: string; + encryptionKey?: string; }): DB => { - throwSyncApiError('open'); + throwSyncApiError("open"); }; export const openSync = (_params: { - url: string; - authToken: string; - name: string; - location?: string; - libsqlSyncInterval?: number; - libsqlOffline?: boolean; - encryptionKey?: string; - remoteEncryptionKey?: string; + url: string; + authToken: string; + name: string; + location?: string; + libsqlSyncInterval?: number; + libsqlOffline?: boolean; + encryptionKey?: string; + remoteEncryptionKey?: string; }): DB => { - throwSyncApiError('openSync'); + throwSyncApiError("openSync"); }; -export const openRemote = (_params: { - url: string; - authToken: string; -}): DB => { - throw new Error('[op-sqlite] openRemote() is not supported on web.'); +export const openRemote = (_params: { url: string; authToken: string }): DB => { + throw new Error("[op-sqlite] openRemote() is not supported on web."); }; export const moveAssetsDatabase = async (_args: { - filename: string; - path?: string; - overwrite?: boolean; + filename: string; + path?: string; + overwrite?: boolean; }): Promise => { - throw new Error('[op-sqlite] moveAssetsDatabase() is not supported on web.'); + throw new Error("[op-sqlite] moveAssetsDatabase() is not supported on web."); }; export const getDylibPath = (_bundle: string, _name: string): string => { - throw new Error('[op-sqlite] getDylibPath() is not supported on web.'); + throw new Error("[op-sqlite] getDylibPath() is not supported on web."); }; export const isSQLCipher = (): boolean => { - return false; + return false; }; export const isLibsql = (): boolean => { - return false; + return false; }; export const isIOSEmbedded = (): boolean => { - return false; + return false; }; /** diff --git a/src/sqlite-wasm-optional.d.ts b/src/sqlite-wasm-optional.d.ts new file mode 100644 index 00000000..d455569b --- /dev/null +++ b/src/sqlite-wasm-optional.d.ts @@ -0,0 +1,3 @@ +declare module "@sqlite.org/sqlite-wasm" { + export const sqlite3Worker1Promiser: any; +} diff --git a/src/types.ts b/src/types.ts index 07b42f5f..ce03b4f2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,10 @@ export type Scalar = - | string - | number - | boolean - | null - | ArrayBuffer - | ArrayBufferView; + | string + | number + | boolean + | null + | ArrayBuffer + | ArrayBufferView; /** * Object returned by SQL Query executions { @@ -17,17 +17,17 @@ export type Scalar = * @interface QueryResult */ export type QueryResult = { - insertId?: number; - rowsAffected: number; - res?: any[]; - rows: Array>; - // An array of intermediate results, just values without column names - rawRows?: Scalar[][]; - columnNames?: string[]; - /** - * Query metadata, available only for select query results - */ - metadata?: ColumnMetadata[]; + insertId?: number; + rowsAffected: number; + res?: any[]; + rows: Array>; + // An array of intermediate results, just values without column names + rawRows?: Scalar[][]; + columnNames?: string[]; + /** + * Query metadata, available only for select query results + */ + metadata?: ColumnMetadata[]; }; /** @@ -35,13 +35,13 @@ export type QueryResult = { * Describes some information about columns fetched by the query */ export type ColumnMetadata = { - /** The name used for this column for this result set */ - name: string; - /** The declared column type for this column, when fetched directly from a table or a View resulting from a table column. "UNKNOWN" for dynamic values, like function returned ones. */ - type: string; - /** - * The index for this column for this result set*/ - index: number; + /** The name used for this column for this result set */ + name: string; + /** The declared column type for this column, when fetched directly from a table or a View resulting from a table column. "UNKNOWN" for dynamic values, like function returned ones. */ + type: string; + /** + * The index for this column for this result set*/ + index: number; }; /** @@ -51,11 +51,11 @@ export type ColumnMetadata = { * to declare it a single time, and use an array of array parameters. */ export type SQLBatchTuple = - | [string] - | [string, Scalar[]] - | [string, Scalar[][]]; + | [string] + | [string, Scalar[]] + | [string, Scalar[][]]; -export type UpdateHookOperation = 'INSERT' | 'DELETE' | 'UPDATE'; +export type UpdateHookOperation = "INSERT" | "DELETE" | "UPDATE"; /** * status: 0 or undefined for correct execution, 1 for error @@ -63,7 +63,7 @@ export type UpdateHookOperation = 'INSERT' | 'DELETE' | 'UPDATE'; * rowsAffected: Number of affected rows if status == 0 */ export type BatchQueryResult = { - rowsAffected?: number; + rowsAffected?: number; }; /** @@ -71,257 +71,257 @@ export type BatchQueryResult = { * Similar to BatchQueryResult */ export type FileLoadResult = BatchQueryResult & { - commands?: number; + commands?: number; }; export type Transaction = { - commit: () => Promise; - execute: (query: string, params?: Scalar[]) => Promise; - rollback: () => QueryResult; + commit: () => Promise; + execute: (query: string, params?: Scalar[]) => Promise; + rollback: () => QueryResult; }; export type _PendingTransaction = { - /* - * The start function should not throw or return a promise because the - * queue just calls it and does not monitor for failures or completions. - * - * It should catch any errors and call the resolve or reject of the wrapping - * promise when complete. - * - * It should also automatically commit or rollback the transaction if needed - */ - start: () => void; + /* + * The start function should not throw or return a promise because the + * queue just calls it and does not monitor for failures or completions. + * + * It should catch any errors and call the resolve or reject of the wrapping + * promise when complete. + * + * It should also automatically commit or rollback the transaction if needed + */ + start: () => void; }; export type PreparedStatement = { - bind: (params: any[]) => Promise; - bindSync: (params: any[]) => void; - execute: () => Promise; + bind: (params: any[]) => Promise; + bindSync: (params: any[]) => void; + execute: () => Promise; }; export type _InternalDB = { - close: () => void; - closeAsync?: () => Promise; - delete: (location?: string) => void; - attach: (params: { - secondaryDbFileName: string; - alias: string; - location?: string; - }) => void; - detach: (alias: string) => void; - transaction: (fn: (tx: Transaction) => Promise) => Promise; - executeSync: (query: string, params?: Scalar[]) => QueryResult; - execute: (query: string, params?: Scalar[]) => Promise; - executeWithHostObjects: ( - query: string, - params?: Scalar[] - ) => Promise; - executeBatch: (commands: SQLBatchTuple[]) => Promise; - loadFile: (location: string) => Promise; - updateHook: ( - callback?: - | ((params: { - table: string; - operation: UpdateHookOperation; - row?: any; - rowId: number; - }) => void) - | null - ) => void; - commitHook: (callback?: (() => void) | null) => void; - rollbackHook: (callback?: (() => void) | null) => void; - prepareStatement: (query: string) => PreparedStatement; - loadExtension: (path: string, entryPoint?: string) => void; - executeRaw: (query: string, params?: Scalar[]) => Promise; - executeRawSync: (query: string, params?: Scalar[]) => any[]; - getDbPath: (location?: string) => string; - reactiveExecute: (params: { - query: string; - arguments: any[]; - fireOn: { - table: string; - ids?: number[]; - }[]; - callback: (response: any) => void; - }) => () => void; - sync: () => void; - setReservedBytes: (reservedBytes: number) => void; - getReservedBytes: () => number; - flushPendingReactiveQueries: () => Promise; + close: () => void; + closeAsync?: () => Promise; + delete: (location?: string) => void; + attach: (params: { + secondaryDbFileName: string; + alias: string; + location?: string; + }) => void; + detach: (alias: string) => void; + transaction: (fn: (tx: Transaction) => Promise) => Promise; + executeSync: (query: string, params?: Scalar[]) => QueryResult; + execute: (query: string, params?: Scalar[]) => Promise; + executeWithHostObjects: ( + query: string, + params?: Scalar[], + ) => Promise; + executeBatch: (commands: SQLBatchTuple[]) => Promise; + loadFile: (location: string) => Promise; + updateHook: ( + callback?: + | ((params: { + table: string; + operation: UpdateHookOperation; + row?: any; + rowId: number; + }) => void) + | null, + ) => void; + commitHook: (callback?: (() => void) | null) => void; + rollbackHook: (callback?: (() => void) | null) => void; + prepareStatement: (query: string) => PreparedStatement; + loadExtension: (path: string, entryPoint?: string) => void; + executeRaw: (query: string, params?: Scalar[]) => Promise; + executeRawSync: (query: string, params?: Scalar[]) => any[]; + getDbPath: (location?: string) => string; + reactiveExecute: (params: { + query: string; + arguments: any[]; + fireOn: { + table: string; + ids?: number[]; + }[]; + callback: (response: any) => void; + }) => () => void; + sync: () => void; + setReservedBytes: (reservedBytes: number) => void; + getReservedBytes: () => number; + flushPendingReactiveQueries: () => Promise; }; export type DB = { - close: () => void; - closeAsync: () => Promise; - delete: (location?: string) => void; - attach: (params: { - secondaryDbFileName: string; - alias: string; - location?: string; - }) => void; - detach: (alias: string) => void; - /** - * Wraps all the executions into a transaction. If an error is thrown it will rollback all of the changes - * - * You need to use this if you are using reactive queries for the queries to fire after the transaction is done - */ - transaction: (fn: (tx: Transaction) => Promise) => Promise; - /** - * Sync version of the execute function - * It will block the JS thread and therefore your UI and should be used with caution - * - * When writing your queries, you can use the ? character as a placeholder for parameters - * The parameters will be automatically escaped and sanitized - * - * Example: - * db.executeSync('SELECT * FROM table WHERE id = ?', [1]); - * - * If you are writing a query that doesn't require parameters, you can omit the second argument - * - * If you are writing to the database YOU SHOULD BE USING TRANSACTIONS! - * Transactions protect you from partial writes and ensure that your data is always in a consistent state - * - * @param query - * @param params - * @returns QueryResult - */ - executeSync: (query: string, params?: Scalar[]) => QueryResult; - /** - * Basic query execution function, it is async don't forget to await it - * - * When writing your queries, you can use the ? character as a placeholder for parameters - * The parameters will be automatically escaped and sanitized - * - * Example: - * await db.execute('SELECT * FROM table WHERE id = ?', [1]); - * - * If you are writing a query that doesn't require parameters, you can omit the second argument - * - * If you are writing to the database YOU SHOULD BE USING TRANSACTIONS! - * Transactions protect you from partial writes and ensure that your data is always in a consistent state - * - * If you need a large amount of queries ran as fast as possible you should be using `executeBatch`, `executeRaw`, `loadFile` or `executeWithHostObjects` - * - * @param query string of your SQL query - * @param params a list of parameters to bind to the query, if any - * @returns Promise with the result of the query - */ - execute: (query: string, params?: Scalar[]) => Promise; - /** - * Similar to the execute function but returns the response in HostObjects - * Read more about HostObjects in the documentation and their pitfalls - * - * Will be a lot faster than the normal execute functions when returning data but you will pay when accessing the fields - * as the conversion is done the moment you access any field - * @param query - * @param params - * @returns - */ - executeWithHostObjects: ( - query: string, - params?: Scalar[] - ) => Promise; - /** - * Executes all the queries in the params inside a single transaction - * - * It's faster than executing single queries as data is sent to the native side only once - * @param commands - * @returns Promise - */ - executeBatch: (commands: SQLBatchTuple[]) => Promise; - /** - * Loads a SQLite Dump from disk. It will be the fastest way to execute a large set of queries as no JS is involved - */ - loadFile: (location: string) => Promise; - updateHook: ( - callback?: - | ((params: { - table: string; - operation: UpdateHookOperation; - row?: any; - rowId: number; - }) => void) - | null - ) => void; - commitHook: (callback?: (() => void) | null) => void; - rollbackHook: (callback?: (() => void) | null) => void; - /** - * Constructs a prepared statement from the query string - * The statement can be re-bound with parameters and executed - * The performance gain is significant when the same query is executed multiple times, NOT when the query is executed (once) - * The cost lies in the preparation of the statement as it is compiled and optimized by the sqlite engine, the params can then rebound - * but the query itself is already optimized - * - * @param query string of your SQL query - * @returns Prepared statement object - */ - prepareStatement: (query: string) => PreparedStatement; - /** - * Loads a runtime loadable sqlite extension. Libsql and iOS embedded version do not support loading extensions - */ - loadExtension: (path: string, entryPoint?: string) => void; - /** - * Same as `execute` except the results are not returned in objects but rather in arrays with just the values and not the keys - * It will be faster since a lot of repeated work is skipped and only the values you care about are returned - */ - executeRaw: (query: string, params?: Scalar[]) => Promise; - /** - * Same as `executeRaw` but it will block the JS thread and therefore your UI and should be used with caution - * It will return an array of arrays with just the values and not the keys - */ - executeRawSync: (query: string, params?: Scalar[]) => any[]; - /** - * Gets the absolute path to the db file. Useful for debugging on local builds and for attaching the DB from users devices - */ - getDbPath: (location?: string) => string; - /** - * Reactive execution of queries when data is written to the database. Check the docs for how to use them. - */ - reactiveExecute: (params: { - query: string; - arguments: any[]; - fireOn: { - table: string; - ids?: number[]; - }[]; - callback: (response: any) => void; - }) => () => void; - /** This function is only available for libsql. - * Allows to trigger a sync the database with it's remote replica - * In order for this function to work you need to use openSync or openRemote functions - * with libsql: true in the package.json - * - * The database is hosted in turso - **/ - sync: () => void; - setReservedBytes: (reservedBytes: number) => void; - getReservedBytes: () => number; - /** - * If you have changed any of the tables outside of a transaction then the reactive queries will not fire on their own - * This method allows to flush the pending queue of changes. Useful when using Drizzle or other ORM that do not - * use the db.transaction method internally - * @returns void - */ - flushPendingReactiveQueries: () => Promise; + close: () => void; + closeAsync: () => Promise; + delete: (location?: string) => void; + attach: (params: { + secondaryDbFileName: string; + alias: string; + location?: string; + }) => void; + detach: (alias: string) => void; + /** + * Wraps all the executions into a transaction. If an error is thrown it will rollback all of the changes + * + * You need to use this if you are using reactive queries for the queries to fire after the transaction is done + */ + transaction: (fn: (tx: Transaction) => Promise) => Promise; + /** + * Sync version of the execute function + * It will block the JS thread and therefore your UI and should be used with caution + * + * When writing your queries, you can use the ? character as a placeholder for parameters + * The parameters will be automatically escaped and sanitized + * + * Example: + * db.executeSync('SELECT * FROM table WHERE id = ?', [1]); + * + * If you are writing a query that doesn't require parameters, you can omit the second argument + * + * If you are writing to the database YOU SHOULD BE USING TRANSACTIONS! + * Transactions protect you from partial writes and ensure that your data is always in a consistent state + * + * @param query + * @param params + * @returns QueryResult + */ + executeSync: (query: string, params?: Scalar[]) => QueryResult; + /** + * Basic query execution function, it is async don't forget to await it + * + * When writing your queries, you can use the ? character as a placeholder for parameters + * The parameters will be automatically escaped and sanitized + * + * Example: + * await db.execute('SELECT * FROM table WHERE id = ?', [1]); + * + * If you are writing a query that doesn't require parameters, you can omit the second argument + * + * If you are writing to the database YOU SHOULD BE USING TRANSACTIONS! + * Transactions protect you from partial writes and ensure that your data is always in a consistent state + * + * If you need a large amount of queries ran as fast as possible you should be using `executeBatch`, `executeRaw`, `loadFile` or `executeWithHostObjects` + * + * @param query string of your SQL query + * @param params a list of parameters to bind to the query, if any + * @returns Promise with the result of the query + */ + execute: (query: string, params?: Scalar[]) => Promise; + /** + * Similar to the execute function but returns the response in HostObjects + * Read more about HostObjects in the documentation and their pitfalls + * + * Will be a lot faster than the normal execute functions when returning data but you will pay when accessing the fields + * as the conversion is done the moment you access any field + * @param query + * @param params + * @returns + */ + executeWithHostObjects: ( + query: string, + params?: Scalar[], + ) => Promise; + /** + * Executes all the queries in the params inside a single transaction + * + * It's faster than executing single queries as data is sent to the native side only once + * @param commands + * @returns Promise + */ + executeBatch: (commands: SQLBatchTuple[]) => Promise; + /** + * Loads a SQLite Dump from disk. It will be the fastest way to execute a large set of queries as no JS is involved + */ + loadFile: (location: string) => Promise; + updateHook: ( + callback?: + | ((params: { + table: string; + operation: UpdateHookOperation; + row?: any; + rowId: number; + }) => void) + | null, + ) => void; + commitHook: (callback?: (() => void) | null) => void; + rollbackHook: (callback?: (() => void) | null) => void; + /** + * Constructs a prepared statement from the query string + * The statement can be re-bound with parameters and executed + * The performance gain is significant when the same query is executed multiple times, NOT when the query is executed (once) + * The cost lies in the preparation of the statement as it is compiled and optimized by the sqlite engine, the params can then rebound + * but the query itself is already optimized + * + * @param query string of your SQL query + * @returns Prepared statement object + */ + prepareStatement: (query: string) => PreparedStatement; + /** + * Loads a runtime loadable sqlite extension. Libsql and iOS embedded version do not support loading extensions + */ + loadExtension: (path: string, entryPoint?: string) => void; + /** + * Same as `execute` except the results are not returned in objects but rather in arrays with just the values and not the keys + * It will be faster since a lot of repeated work is skipped and only the values you care about are returned + */ + executeRaw: (query: string, params?: Scalar[]) => Promise; + /** + * Same as `executeRaw` but it will block the JS thread and therefore your UI and should be used with caution + * It will return an array of arrays with just the values and not the keys + */ + executeRawSync: (query: string, params?: Scalar[]) => any[]; + /** + * Gets the absolute path to the db file. Useful for debugging on local builds and for attaching the DB from users devices + */ + getDbPath: (location?: string) => string; + /** + * Reactive execution of queries when data is written to the database. Check the docs for how to use them. + */ + reactiveExecute: (params: { + query: string; + arguments: any[]; + fireOn: { + table: string; + ids?: number[]; + }[]; + callback: (response: any) => void; + }) => () => void; + /** This function is only available for libsql. + * Allows to trigger a sync the database with it's remote replica + * In order for this function to work you need to use openSync or openRemote functions + * with libsql: true in the package.json + * + * The database is hosted in turso + **/ + sync: () => void; + setReservedBytes: (reservedBytes: number) => void; + getReservedBytes: () => number; + /** + * If you have changed any of the tables outside of a transaction then the reactive queries will not fire on their own + * This method allows to flush the pending queue of changes. Useful when using Drizzle or other ORM that do not + * use the db.transaction method internally + * @returns void + */ + flushPendingReactiveQueries: () => Promise; }; export type DBParams = { - url?: string; - authToken?: string; - name?: string; - location?: string; - syncInterval?: number; + url?: string; + authToken?: string; + name?: string; + location?: string; + syncInterval?: number; }; export type OPSQLiteProxy = { - open: (options: { - name: string; - location?: string; - encryptionKey?: string; - }) => _InternalDB; - openRemote: (options: { url: string; authToken: string }) => _InternalDB; - openSync: (options: DBParams) => _InternalDB; - isSQLCipher: () => boolean; - isLibsql: () => boolean; - isIOSEmbedded: () => boolean; + open: (options: { + name: string; + location?: string; + encryptionKey?: string; + }) => _InternalDB; + openRemote: (options: { url: string; authToken: string }) => _InternalDB; + openSync: (options: DBParams) => _InternalDB; + isSQLCipher: () => boolean; + isLibsql: () => boolean; + isIOSEmbedded: () => boolean; }; diff --git a/yarn.lock b/yarn.lock index 02827e7f..56a957e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1523,6 +1523,97 @@ __metadata: languageName: node linkType: hard +"@biomejs/biome@npm:^2.4.10": + version: 2.4.10 + resolution: "@biomejs/biome@npm:2.4.10" + dependencies: + "@biomejs/cli-darwin-arm64": "npm:2.4.10" + "@biomejs/cli-darwin-x64": "npm:2.4.10" + "@biomejs/cli-linux-arm64": "npm:2.4.10" + "@biomejs/cli-linux-arm64-musl": "npm:2.4.10" + "@biomejs/cli-linux-x64": "npm:2.4.10" + "@biomejs/cli-linux-x64-musl": "npm:2.4.10" + "@biomejs/cli-win32-arm64": "npm:2.4.10" + "@biomejs/cli-win32-x64": "npm:2.4.10" + dependenciesMeta: + "@biomejs/cli-darwin-arm64": + optional: true + "@biomejs/cli-darwin-x64": + optional: true + "@biomejs/cli-linux-arm64": + optional: true + "@biomejs/cli-linux-arm64-musl": + optional: true + "@biomejs/cli-linux-x64": + optional: true + "@biomejs/cli-linux-x64-musl": + optional: true + "@biomejs/cli-win32-arm64": + optional: true + "@biomejs/cli-win32-x64": + optional: true + bin: + biome: bin/biome + checksum: 10c0/80d10d5e6fa41a24efb9020ee73b79b0aca46942b55ea96e880c3bb45ea14c71e49fb1be9f134bee23b2d940bb8cad51a70351ca051e09a43613018dba693bd6 + languageName: node + linkType: hard + +"@biomejs/cli-darwin-arm64@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-darwin-arm64@npm:2.4.10" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@biomejs/cli-darwin-x64@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-darwin-x64@npm:2.4.10" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@biomejs/cli-linux-arm64-musl@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-linux-arm64-musl@npm:2.4.10" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@biomejs/cli-linux-arm64@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-linux-arm64@npm:2.4.10" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@biomejs/cli-linux-x64-musl@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-linux-x64-musl@npm:2.4.10" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@biomejs/cli-linux-x64@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-linux-x64@npm:2.4.10" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@biomejs/cli-win32-arm64@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-win32-arm64@npm:2.4.10" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@biomejs/cli-win32-x64@npm:2.4.10": + version: 2.4.10 + resolution: "@biomejs/cli-win32-x64@npm:2.4.10" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.27.7": version: 0.27.7 resolution: "@esbuild/aix-ppc64@npm:0.27.7" @@ -2196,6 +2287,7 @@ __metadata: version: 0.0.0-use.local resolution: "@op-engineering/op-sqlite@workspace:." dependencies: + "@biomejs/biome": "npm:^2.4.10" "@sqlite.org/sqlite-wasm": "npm:^3.51.2-build8" "@types/better-sqlite3": "npm:^7.6.13" "@types/jest": "npm:^30.0.0" @@ -2207,8 +2299,12 @@ __metadata: react-native-builder-bob: "npm:^0.40.15" typescript: "npm:^5.9.2" peerDependencies: + "@sqlite.org/sqlite-wasm": "*" react: "*" react-native: "*" + peerDependenciesMeta: + "@sqlite.org/sqlite-wasm": + optional: true languageName: unknown linkType: soft @@ -7421,6 +7517,7 @@ __metadata: "@react-native/babel-preset": "npm:0.82.1" "@react-native/metro-config": "npm:0.82.1" "@react-native/typescript-config": "npm:0.81.5" + "@sqlite.org/sqlite-wasm": "npm:^3.51.2-build8" "@types/chance": "npm:^1.1.7" "@types/react": "npm:^19.1.1" "@vitejs/plugin-react": "npm:^5.1.0"

O%sgbWed-C0+wF{6s-$l2;%VRk@yZ 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 literal 0 HcmV?d00001 diff --git a/yarn.lock b/yarn.lock index eabe566c..0195d11b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,7 +46,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.29.0": version: 7.29.0 resolution: "@babel/core@npm:7.29.0" dependencies: @@ -1114,7 +1114,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-self@npm:^7.24.7": +"@babel/plugin-transform-react-jsx-self@npm:^7.24.7, @babel/plugin-transform-react-jsx-self@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-react-jsx-self@npm:7.27.1" dependencies: @@ -1125,7 +1125,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-source@npm:^7.24.7": +"@babel/plugin-transform-react-jsx-source@npm:^7.24.7, @babel/plugin-transform-react-jsx-source@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-react-jsx-source@npm:7.27.1" dependencies: @@ -1466,6 +1466,13 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.18.6": + version: 7.29.2 + resolution: "@babel/runtime@npm:7.29.2" + checksum: 10c0/30b80a0140d16467792e1bbeb06f655b0dab70407da38dfac7fedae9c859f9ae9d846ef14ad77bd3814c064295fe9b1bc551f1541ea14646ae9f22b71a8bc17a + languageName: node + linkType: hard + "@babel/runtime@npm:^7.25.0": version: 7.28.6 resolution: "@babel/runtime@npm:7.28.6" @@ -1516,6 +1523,188 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/aix-ppc64@npm:0.27.7" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm64@npm:0.27.7" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm@npm:0.27.7" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-x64@npm:0.27.7" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-arm64@npm:0.27.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-x64@npm:0.27.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-arm64@npm:0.27.7" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-x64@npm:0.27.7" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm64@npm:0.27.7" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm@npm:0.27.7" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ia32@npm:0.27.7" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-loong64@npm:0.27.7" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-mips64el@npm:0.27.7" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ppc64@npm:0.27.7" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-riscv64@npm:0.27.7" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-s390x@npm:0.27.7" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-x64@npm:0.27.7" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-arm64@npm:0.27.7" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-x64@npm:0.27.7" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-arm64@npm:0.27.7" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-x64@npm:0.27.7" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openharmony-arm64@npm:0.27.7" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/sunos-x64@npm:0.27.7" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-arm64@npm:0.27.7" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-ia32@npm:0.27.7" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-x64@npm:0.27.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@gar/promise-retry@npm:^1.0.0": version: 1.0.2 resolution: "@gar/promise-retry@npm:1.0.2" @@ -2490,6 +2679,13 @@ __metadata: languageName: node linkType: hard +"@react-native/normalize-colors@npm:^0.74.1": + version: 0.74.89 + resolution: "@react-native/normalize-colors@npm:0.74.89" + checksum: 10c0/6d0e5c91793ca5a66b4a0e5995361f474caacac56bde4772ac02b8ab470bd323076c567bd8856b0b097816d2b890e73a4040a3df01fd284adee683f5ba89d5ba + languageName: node + linkType: hard + "@react-native/typescript-config@npm:0.81.5": version: 0.81.5 resolution: "@react-native/typescript-config@npm:0.81.5" @@ -2514,6 +2710,188 @@ __metadata: languageName: node linkType: hard +"@rolldown/pluginutils@npm:1.0.0-rc.3": + version: 1.0.0-rc.3 + resolution: "@rolldown/pluginutils@npm:1.0.0-rc.3" + checksum: 10c0/3928b6282a30f307d1b075d2f217180ae173ea9e00638ce46ab65f089bd5f7a0b2c488ae1ce530f509387793c656a2910337c4cd68fa9d37d7e439365989e699 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.60.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-android-arm64@npm:4.60.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.60.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.60.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.60.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-freebsd-x64@npm:4.60.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.60.1" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.60.1" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.60.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.60.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.60.1" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.60.1" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.60.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.60.1" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.60.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.60.1" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.60.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.60.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.60.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-openbsd-x64@npm:4.60.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.60.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.60.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.60.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.60.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.60.1": + version: 4.60.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.60.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@sideway/address@npm:^4.1.5": version: 4.1.5 resolution: "@sideway/address@npm:4.1.5" @@ -2569,7 +2947,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14": +"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" dependencies: @@ -2626,6 +3004,13 @@ __metadata: languageName: node linkType: hard +"@types/estree@npm:1.0.8": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 + languageName: node + linkType: hard + "@types/graceful-fs@npm:^4.1.3": version: 4.1.9 resolution: "@types/graceful-fs@npm:4.1.9" @@ -2730,6 +3115,22 @@ __metadata: languageName: node linkType: hard +"@vitejs/plugin-react@npm:^5.1.0": + version: 5.2.0 + resolution: "@vitejs/plugin-react@npm:5.2.0" + dependencies: + "@babel/core": "npm:^7.29.0" + "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1" + "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1" + "@rolldown/pluginutils": "npm:1.0.0-rc.3" + "@types/babel__core": "npm:^7.20.5" + react-refresh: "npm:^0.18.0" + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10c0/bac0a409e71eee954a05bc41580411c369bd5f9ef0586a1f9743fba76ad6603c437d93d407d230780015361f93d1592c55e53314813cded6369c36d3c1e8edbf + languageName: node + linkType: hard + "@vscode/sudo-prompt@npm:^9.0.0": version: 9.3.2 resolution: "@vscode/sudo-prompt@npm:9.3.2" @@ -2960,7 +3361,7 @@ __metadata: languageName: node linkType: hard -"asap@npm:~2.0.6": +"asap@npm:~2.0.3, asap@npm:~2.0.6": version: 2.0.6 resolution: "asap@npm:2.0.6" checksum: 10c0/c6d5e39fe1f15e4b87677460bd66b66050cd14c772269cee6688824c1410a08ab20254bb6784f9afb75af9144a9f9a7692d49547f4d19d715aeb7c0318f3136d @@ -3799,6 +4200,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:^3.1.5": + version: 3.2.0 + resolution: "cross-fetch@npm:3.2.0" + dependencies: + node-fetch: "npm:^2.7.0" + checksum: 10c0/d8596adf0269130098a676f6739a0922f3cc7b71cc89729925411ebe851a87026171c82ea89154c4811c9867c01c44793205a52e618ce2684650218c7fbeeb9f + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" @@ -3810,6 +4220,15 @@ __metadata: languageName: node linkType: hard +"css-in-js-utils@npm:^3.1.0": + version: 3.1.0 + resolution: "css-in-js-utils@npm:3.1.0" + dependencies: + hyphenate-style-name: "npm:^1.0.3" + checksum: 10c0/8bb042e8f7701a7edadc3cce5ce2d5cf41189631d7e2aed194d5a7059b25776dded2a0466cb9da1d1f3fc6c99dcecb51e45671148d073b8a2a71e34755152e52 + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -4140,6 +4559,95 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.27.0": + version: 0.27.7 + resolution: "esbuild@npm:0.27.7" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.7" + "@esbuild/android-arm": "npm:0.27.7" + "@esbuild/android-arm64": "npm:0.27.7" + "@esbuild/android-x64": "npm:0.27.7" + "@esbuild/darwin-arm64": "npm:0.27.7" + "@esbuild/darwin-x64": "npm:0.27.7" + "@esbuild/freebsd-arm64": "npm:0.27.7" + "@esbuild/freebsd-x64": "npm:0.27.7" + "@esbuild/linux-arm": "npm:0.27.7" + "@esbuild/linux-arm64": "npm:0.27.7" + "@esbuild/linux-ia32": "npm:0.27.7" + "@esbuild/linux-loong64": "npm:0.27.7" + "@esbuild/linux-mips64el": "npm:0.27.7" + "@esbuild/linux-ppc64": "npm:0.27.7" + "@esbuild/linux-riscv64": "npm:0.27.7" + "@esbuild/linux-s390x": "npm:0.27.7" + "@esbuild/linux-x64": "npm:0.27.7" + "@esbuild/netbsd-arm64": "npm:0.27.7" + "@esbuild/netbsd-x64": "npm:0.27.7" + "@esbuild/openbsd-arm64": "npm:0.27.7" + "@esbuild/openbsd-x64": "npm:0.27.7" + "@esbuild/openharmony-arm64": "npm:0.27.7" + "@esbuild/sunos-x64": "npm:0.27.7" + "@esbuild/win32-arm64": "npm:0.27.7" + "@esbuild/win32-ia32": "npm:0.27.7" + "@esbuild/win32-x64": "npm:0.27.7" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/ccd51f0555708bc9ff4ec9dc3ac92d3daacd45ecaac949ca8645984c5c323bf8cefe98c2df307418685e0b4ce37f9a3bdbfe8e3651fe632a0059a436195a17d4 + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -4353,6 +4861,28 @@ __metadata: languageName: node linkType: hard +"fbjs-css-vars@npm:^1.0.0": + version: 1.0.2 + resolution: "fbjs-css-vars@npm:1.0.2" + checksum: 10c0/dfb64116b125a64abecca9e31477b5edb9a2332c5ffe74326fe36e0a72eef7fc8a49b86adf36c2c293078d79f4524f35e80f5e62546395f53fb7c9e69821f54f + languageName: node + linkType: hard + +"fbjs@npm:^3.0.4": + version: 3.0.5 + resolution: "fbjs@npm:3.0.5" + dependencies: + cross-fetch: "npm:^3.1.5" + fbjs-css-vars: "npm:^1.0.0" + loose-envify: "npm:^1.0.0" + object-assign: "npm:^4.1.0" + promise: "npm:^7.1.1" + setimmediate: "npm:^1.0.5" + ua-parser-js: "npm:^1.0.35" + checksum: 10c0/66d0a2fc9a774f9066e35ac2ac4bf1245931d27f3ac287c7d47e6aa1fc152b243c2109743eb8f65341e025621fb51a12038fadb9fd8fda2e3ddae04ebab06f91 + languageName: node + linkType: hard + "fdir@npm:^6.5.0": version: 6.5.0 resolution: "fdir@npm:6.5.0" @@ -4494,7 +5024,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -4504,7 +5034,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -4855,6 +5385,13 @@ __metadata: languageName: node linkType: hard +"hyphenate-style-name@npm:^1.0.3": + version: 1.1.0 + resolution: "hyphenate-style-name@npm:1.1.0" + checksum: 10c0/bfe88deac2414a41a0d08811e277c8c098f23993d6a1eb17f14a0f11b54c4d42865a63d3cfe1914668eefb9a188e2de58f38b55a179a238fd1fef606893e194f + languageName: node + linkType: hard + "iconv-lite@npm:^0.7.2": version: 0.7.2 resolution: "iconv-lite@npm:0.7.2" @@ -4958,6 +5495,15 @@ __metadata: languageName: node linkType: hard +"inline-style-prefixer@npm:^7.0.1": + version: 7.0.1 + resolution: "inline-style-prefixer@npm:7.0.1" + dependencies: + css-in-js-utils: "npm:^3.1.0" + checksum: 10c0/15da5a396b7f286b5b6742efe315218cd577bc96b43de08aeb76af7697d9f1ab3bfc66cf19fad2173957dd5d617a790240b9d51898bdcf4c2efb40d3f8bcb370 + languageName: node + linkType: hard + "invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -6137,6 +6683,13 @@ __metadata: languageName: node linkType: hard +"memoize-one@npm:^6.0.0": + version: 6.0.0 + resolution: "memoize-one@npm:6.0.0" + checksum: 10c0/45c88e064fd715166619af72e8cf8a7a17224d6edf61f7a8633d740ed8c8c0558a4373876c9b8ffc5518c2b65a960266adf403cc215cb1e90f7e262b58991f54 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -6667,6 +7220,20 @@ __metadata: languageName: node linkType: hard +"node-fetch@npm:^2.7.0": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 12.2.0 resolution: "node-gyp@npm:12.2.0" @@ -6761,7 +7328,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1": +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -6848,17 +7415,21 @@ __metadata: "@react-native/typescript-config": "npm:0.81.5" "@types/chance": "npm:^1.1.7" "@types/react": "npm:^19.1.1" + "@vitejs/plugin-react": "npm:^5.1.0" chance: "npm:^1.1.9" clsx: "npm:^2.0.0" events: "npm:^3.3.0" patch-package: "npm:^8.0.1" react: "npm:19.1.1" + react-dom: "npm:19.1.1" react-native: "npm:0.82.1" react-native-builder-bob: "npm:^0.40.13" react-native-monorepo-config: "npm:^0.1.9" react-native-restart: "npm:^0.0.27" react-native-safe-area-context: "npm:^5.6.2" + react-native-web: "npm:^0.21.2" tailwindcss: "npm:3.3.2" + vite: "npm:^7.1.9" languageName: unknown linkType: soft @@ -7185,7 +7756,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.23": +"postcss@npm:^8.4.23, postcss@npm:^8.5.6": version: 8.5.8 resolution: "postcss@npm:8.5.8" dependencies: @@ -7259,6 +7830,15 @@ __metadata: languageName: node linkType: hard +"promise@npm:^7.1.1": + version: 7.3.1 + resolution: "promise@npm:7.3.1" + dependencies: + asap: "npm:~2.0.3" + checksum: 10c0/742e5c0cc646af1f0746963b8776299701ad561ce2c70b49365d62c8db8ea3681b0a1bf0d4e2fe07910bf72f02d39e51e8e73dc8d7503c3501206ac908be107f + languageName: node + linkType: hard + "promise@npm:^8.3.0": version: 8.3.0 resolution: "promise@npm:8.3.0" @@ -7363,6 +7943,17 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:19.1.1": + version: 19.1.1 + resolution: "react-dom@npm:19.1.1" + dependencies: + scheduler: "npm:^0.26.0" + peerDependencies: + react: ^19.1.1 + checksum: 10c0/8c91198510521299c56e4e8d5e3a4508b2734fb5e52f29eeac33811de64e76fe586ad32c32182e2e84e070d98df67125da346c3360013357228172dbcd20bcdd + languageName: node + linkType: hard + "react-is@npm:^17.0.1": version: 17.0.2 resolution: "react-is@npm:17.0.2" @@ -7449,6 +8040,25 @@ __metadata: languageName: node linkType: hard +"react-native-web@npm:^0.21.2": + version: 0.21.2 + resolution: "react-native-web@npm:0.21.2" + dependencies: + "@babel/runtime": "npm:^7.18.6" + "@react-native/normalize-colors": "npm:^0.74.1" + fbjs: "npm:^3.0.4" + inline-style-prefixer: "npm:^7.0.1" + memoize-one: "npm:^6.0.0" + nullthrows: "npm:^1.1.1" + postcss-value-parser: "npm:^4.2.0" + styleq: "npm:^0.1.3" + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10c0/8c184fef0045c25deff765c8e80963454a5dffd8e389a9e11cf2fec9e769ff0f82c3d56d082b1897a7ded8374d9ae8a49dac7f09377a104f1995a5ddea645095 + languageName: node + linkType: hard + "react-native@npm:0.82.1": version: 0.82.1 resolution: "react-native@npm:0.82.1" @@ -7507,6 +8117,13 @@ __metadata: languageName: node linkType: hard +"react-refresh@npm:^0.18.0": + version: 0.18.0 + resolution: "react-refresh@npm:0.18.0" + checksum: 10c0/34a262f7fd803433a534f50deb27a148112a81adcae440c7d1cbae7ef14d21ea8f2b3d783e858cb7698968183b77755a38b4d4b5b1d79b4f4689c2f6d358fff2 + languageName: node + linkType: hard + "react@npm:19.1.1": version: 19.1.1 resolution: "react@npm:19.1.1" @@ -7703,6 +8320,96 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.43.0": + version: 4.60.1 + resolution: "rollup@npm:4.60.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.60.1" + "@rollup/rollup-android-arm64": "npm:4.60.1" + "@rollup/rollup-darwin-arm64": "npm:4.60.1" + "@rollup/rollup-darwin-x64": "npm:4.60.1" + "@rollup/rollup-freebsd-arm64": "npm:4.60.1" + "@rollup/rollup-freebsd-x64": "npm:4.60.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.60.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.60.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.60.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.60.1" + "@rollup/rollup-linux-loong64-gnu": "npm:4.60.1" + "@rollup/rollup-linux-loong64-musl": "npm:4.60.1" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.60.1" + "@rollup/rollup-linux-ppc64-musl": "npm:4.60.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.60.1" + "@rollup/rollup-linux-riscv64-musl": "npm:4.60.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.60.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.60.1" + "@rollup/rollup-linux-x64-musl": "npm:4.60.1" + "@rollup/rollup-openbsd-x64": "npm:4.60.1" + "@rollup/rollup-openharmony-arm64": "npm:4.60.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.60.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.60.1" + "@rollup/rollup-win32-x64-gnu": "npm:4.60.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.60.1" + "@types/estree": "npm:1.0.8" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/48d3f2216b5533639b007e6756e2275c7f594e45adee21ce03674aa2e004406c661f8b86c7a0b471c9e889c6a9efbb29240ca0b7673c50e391406c490c309833 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -7726,7 +8433,7 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:0.26.0": +"scheduler@npm:0.26.0, scheduler@npm:^0.26.0": version: 0.26.0 resolution: "scheduler@npm:0.26.0" checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356 @@ -7812,6 +8519,13 @@ __metadata: languageName: node linkType: hard +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: 10c0/5bae81bfdbfbd0ce992893286d49c9693c82b1bcc00dcaaf3a09c8f428fdeacf4190c013598b81875dfac2b08a572422db7df779a99332d0fce186d15a3e4d49 + languageName: node + linkType: hard + "setprototypeof@npm:~1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" @@ -8181,6 +8895,13 @@ __metadata: languageName: node linkType: hard +"styleq@npm:^0.1.3": + version: 0.1.3 + resolution: "styleq@npm:0.1.3" + checksum: 10c0/975d951792e65052f1f6e41aaad46492642ce4922b3dc36d4b49b37c8509f9a776794d8f275360f00116a5e6ab1e31514bdcd5840656c4e3213da6803fa12941 + languageName: node + linkType: hard + "sucrase@npm:^3.32.0": version: 3.35.1 resolution: "sucrase@npm:3.35.1" @@ -8346,7 +9067,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.11, tinyglobby@npm:^0.2.12": +"tinyglobby@npm:^0.2.11, tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15": version: 0.2.15 resolution: "tinyglobby@npm:0.2.15" dependencies: @@ -8386,6 +9107,13 @@ __metadata: languageName: node linkType: hard +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" @@ -8500,6 +9228,15 @@ __metadata: languageName: node linkType: hard +"ua-parser-js@npm:^1.0.35": + version: 1.0.41 + resolution: "ua-parser-js@npm:1.0.41" + bin: + ua-parser-js: script/cli.js + checksum: 10c0/45dc1f7f3ce8248e0e64640d2e29c65c0ea1fc9cb105594de84af80e2a57bba4f718b9376098ca7a5b0ffe240f8995b0fa3714afa9d36861c41370a378f1a274 + languageName: node + linkType: hard + "uglify-js@npm:^3.1.4": version: 3.19.3 resolution: "uglify-js@npm:3.19.3" @@ -8639,6 +9376,61 @@ __metadata: languageName: node linkType: hard +"vite@npm:^7.1.9": + version: 7.3.1 + resolution: "vite@npm:7.3.1" + dependencies: + esbuild: "npm:^0.27.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/5c7548f5f43a23533e53324304db4ad85f1896b1bfd3ee32ae9b866bac2933782c77b350eb2b52a02c625c8ad1ddd4c000df077419410650c982cd97fde8d014 + languageName: node + linkType: hard + "vlq@npm:^1.0.0": version: 1.0.1 resolution: "vlq@npm:1.0.1" @@ -8664,6 +9456,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + "whatwg-fetch@npm:^3.0.0": version: 3.6.20 resolution: "whatwg-fetch@npm:3.6.20" @@ -8671,6 +9470,16 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + "which-module@npm:^2.0.0": version: 2.0.1 resolution: "which-module@npm:2.0.1" From e0e06e5bb1b9a3cc4e9fb1a19028e4422d59ec1b Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Mon, 6 Apr 2026 07:46:04 -0400 Subject: [PATCH 2/4] do not vendor sqlite.wasm --- example/vite.config.ts | 2 +- package.json | 5 +- src/functions.web.ts | 24 +- src/web/sqlite-wasm.d.ts | 3 - src/web/sqlite-wasm/index.mjs | 15645 ---------------- .../sqlite-wasm/sqlite3-opfs-async-proxy.js | 666 - src/web/sqlite-wasm/sqlite3-worker1.mjs | 15461 --------------- src/web/sqlite-wasm/sqlite3.wasm | Bin 859730 -> 0 bytes yarn.lock | 8 + 9 files changed, 21 insertions(+), 31793 deletions(-) delete mode 100644 src/web/sqlite-wasm.d.ts delete mode 100644 src/web/sqlite-wasm/index.mjs delete mode 100644 src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js delete mode 100644 src/web/sqlite-wasm/sqlite3-worker1.mjs delete mode 100644 src/web/sqlite-wasm/sqlite3.wasm diff --git a/example/vite.config.ts b/example/vite.config.ts index 05a085f6..95fde73c 100644 --- a/example/vite.config.ts +++ b/example/vite.config.ts @@ -25,7 +25,7 @@ export default defineConfig({ }, }, optimizeDeps: { - exclude: ['@op-engineering/op-sqlite'], + exclude: ['@op-engineering/op-sqlite', '@sqlite.org/sqlite-wasm'], }, build: { outDir: path.resolve(rootDir, 'web-build'), diff --git a/package.json b/package.json index 2e241001..82b22353 100644 --- a/package.json +++ b/package.json @@ -121,5 +121,8 @@ "type": "turbo-module", "version": "0.52.1" }, - "packageManager": "yarn@4.11.0" + "packageManager": "yarn@4.11.0", + "dependencies": { + "@sqlite.org/sqlite-wasm": "^3.51.2-build8" + } } diff --git a/src/functions.web.ts b/src/functions.web.ts index 3aa6345a..e9d0a07a 100644 --- a/src/functions.web.ts +++ b/src/functions.web.ts @@ -57,24 +57,16 @@ let promiserPromise: Promise | null = null; async function getPromiser(): Promise { if (!promiserPromise) { promiserPromise = (async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore TS declaration is provided via src/web/sqlite-wasm.d.ts - const mod = await import('./web/sqlite-wasm/index.mjs'); - const makePromiser = mod.sqlite3Worker1Promiser as ( - config?: unknown - ) => Promise; - - const WebWorker = (globalThis as any).Worker; - if (!WebWorker) { - throw new Error('[op-sqlite] Web Worker API is not available in this environment.'); + const mod = await import('@sqlite.org/sqlite-wasm'); + const makePromiser = mod.sqlite3Worker1Promiser as any; + + const maybePromiser = makePromiser(); + + if (typeof maybePromiser === 'function') { + return maybePromiser as WorkerPromiser; } - return makePromiser({ - worker: () => - new WebWorker(new URL('./web/sqlite-wasm/sqlite3-worker1.mjs', import.meta.url), { - type: 'module', - }), - }); + return (await maybePromiser) as WorkerPromiser; })(); } diff --git a/src/web/sqlite-wasm.d.ts b/src/web/sqlite-wasm.d.ts deleted file mode 100644 index 30c419e1..00000000 --- a/src/web/sqlite-wasm.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module './sqlite-wasm/index.mjs' { - export const sqlite3Worker1Promiser: (config?: unknown) => Promise; -} diff --git a/src/web/sqlite-wasm/index.mjs b/src/web/sqlite-wasm/index.mjs deleted file mode 100644 index 17ae70cc..00000000 --- a/src/web/sqlite-wasm/index.mjs +++ /dev/null @@ -1,15645 +0,0 @@ -//#region src/bin/sqlite3-worker1-promiser.mjs -/** -Configures an sqlite3 Worker API #1 Worker such that it can be -manipulated via a Promise-based interface and returns a factory -function which returns Promises for communicating with the worker. -This proxy has an _almost_ identical interface to the normal -worker API, with any exceptions documented below. - -It requires a configuration object with the following properties: - -- `worker` (required): a Worker instance which loads -`sqlite3-worker1.js` or a functional equivalent. Note that the -promiser factory replaces the worker.onmessage property. This -config option may alternately be a function, in which case this -function re-assigns this property with the result of calling that -function, enabling delayed instantiation of a Worker. - -- `onready` (optional, but...): this callback is called with no -arguments when the worker fires its initial -'sqlite3-api'/'worker1-ready' message, which it does when -sqlite3.initWorker1API() completes its initialization. This is the -simplest way to tell the worker to kick off work at the earliest -opportunity, and the only way to know when the worker module has -completed loading. The irony of using a callback for this, instead -of returning a promise from sqlite3Worker1Promiser() is not lost on -the developers: see sqlite3Worker1Promiser.v2() which uses a -Promise instead. - -- `onunhandled` (optional): a callback which gets passed the -message event object for any worker.onmessage() events which -are not handled by this proxy. Ideally that "should" never -happen, as this proxy aims to handle all known message types. - -- `generateMessageId` (optional): a function which, when passed an -about-to-be-posted message object, generates a _unique_ message ID -for the message, which this API then assigns as the messageId -property of the message. It _must_ generate unique IDs on each call -so that dispatching can work. If not defined, a default generator -is used (which should be sufficient for most or all cases). - -- `debug` (optional): a console.debug()-style function for logging -information about messages. - -This function returns a stateful factory function with the -following interfaces: - -- Promise function(messageType, messageArgs) -- Promise function({message object}) - -The first form expects the "type" and "args" values for a Worker -message. The second expects an object in the form {type:..., -args:...} plus any other properties the client cares to set. This -function will always set the `messageId` property on the object, -even if it's already set, and will set the `dbId` property to the -current database ID if it is _not_ set in the message object. - -The function throws on error. - -The function installs a temporary message listener, posts a -message to the configured Worker, and handles the message's -response via the temporary message listener. The then() callback -of the returned Promise is passed the `message.data` property from -the resulting message, i.e. the payload from the worker, stripped -of the lower-level event state which the onmessage() handler -receives. - -Example usage: - -``` -const config = {...}; -const sq3Promiser = sqlite3Worker1Promiser(config); -sq3Promiser('open', {filename:"/foo.db"}).then(function(msg){ -console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} -}); -sq3Promiser({type:'close'}).then((msg)=>{ -console.log("close response",msg); // => {type:'close', result: {filename:'/foo.db'}, ...} -}); -``` - -Differences from Worker API #1: - -- exec's {callback: STRING} option does not work via this -interface (it triggers an exception), but {callback: function} -does and works exactly like the STRING form does in the Worker: -the callback is called one time for each row of the result set, -passed the same worker message format as the worker API emits: - -{ -type:typeString, -row:VALUE, -rowNumber:1-based-#, -columnNames: array -} - -Where `typeString` is an internally-synthesized message type string -used temporarily for worker message dispatching. It can be ignored -by all client code except that which tests this API. The `row` -property contains the row result in the form implied by the -`rowMode` option (defaulting to `'array'`). The `rowNumber` is a -1-based integer value incremented by 1 on each call into the -callback. - -At the end of the result set, the same event is fired with -(row=undefined, rowNumber=null) to indicate that the end of the -result set has been reached. The rows arrive via worker-posted -messages, with all the implications of that. - -Notable shortcomings: - -- "v1" of this this API is not suitable for use as an ESM module -because ESM worker modules were not widely supported when it was -developed. For use as an ESM module, see the "v2" interface later -on in this file. -*/ -globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig) { - if (1 === arguments.length && "function" === typeof arguments[0]) { - const f = config; - config = Object.assign(Object.create(null), callee.defaultConfig); - config.onready = f; - } else config = Object.assign(Object.create(null), callee.defaultConfig, config); - const handlerMap = Object.create(null); - const noop = function() {}; - const err = config.onerror || noop; - const debug = config.debug || noop; - const idTypeMap = config.generateMessageId ? void 0 : Object.create(null); - const genMsgId = config.generateMessageId || function(msg) { - return msg.type + "#" + (idTypeMap[msg.type] = (idTypeMap[msg.type] || 0) + 1); - }; - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - if (!config.worker) config.worker = callee.defaultConfig.worker; - if ("function" === typeof config.worker) config.worker = config.worker(); - let dbId; - let promiserFunc; - config.worker.onmessage = function(ev) { - ev = ev.data; - debug("worker1.onmessage", ev); - let msgHandler = handlerMap[ev.messageId]; - if (!msgHandler) { - if (ev && "sqlite3-api" === ev.type && "worker1-ready" === ev.result) { - if (config.onready) config.onready(promiserFunc); - return; - } - msgHandler = handlerMap[ev.type]; - if (msgHandler && msgHandler.onrow) { - msgHandler.onrow(ev); - return; - } - if (config.onunhandled) config.onunhandled(arguments[0]); - else err("sqlite3Worker1Promiser() unhandled worker message:", ev); - return; - } - delete handlerMap[ev.messageId]; - switch (ev.type) { - case "error": - msgHandler.reject(ev); - return; - case "open": - if (!dbId) dbId = ev.dbId; - break; - case "close": - if (ev.dbId === dbId) dbId = void 0; - break; - default: break; - } - try { - msgHandler.resolve(ev); - } catch (e) { - msgHandler.reject(e); - } - }; - return promiserFunc = function() { - let msg; - if (1 === arguments.length) msg = arguments[0]; - else if (2 === arguments.length) { - msg = Object.create(null); - msg.type = arguments[0]; - msg.args = arguments[1]; - msg.dbId = msg.args.dbId; - } else toss("Invalid arguments for sqlite3Worker1Promiser()-created factory."); - if (!msg.dbId && msg.type !== "open") msg.dbId = dbId; - msg.messageId = genMsgId(msg); - msg.departureTime = performance.now(); - const proxy = Object.create(null); - proxy.message = msg; - let rowCallbackId; - if ("exec" === msg.type && msg.args) { - if ("function" === typeof msg.args.callback) { - rowCallbackId = msg.messageId + ":row"; - proxy.onrow = msg.args.callback; - msg.args.callback = rowCallbackId; - handlerMap[rowCallbackId] = proxy; - } else if ("string" === typeof msg.args.callback) toss("exec callback may not be a string when using the Promise interface."); - } - let p = new Promise(function(resolve, reject) { - proxy.resolve = resolve; - proxy.reject = reject; - handlerMap[msg.messageId] = proxy; - debug("Posting", msg.type, "message to Worker dbId=" + (dbId || "default") + ":", msg); - config.worker.postMessage(msg); - }); - if (rowCallbackId) p = p.finally(() => delete handlerMap[rowCallbackId]); - return p; - }; -}; -globalThis.sqlite3Worker1Promiser.defaultConfig = { - worker: function() { - return new Worker(new URL("sqlite3-worker1.mjs", import.meta.url), { type: "module" }); - }, - onerror: (...args) => console.error("sqlite3Worker1Promiser():", ...args) -}; -/** -sqlite3Worker1Promiser.v2(), added in 3.46, works identically to -sqlite3Worker1Promiser() except that it returns a Promise instead -of relying an an onready callback in the config object. The Promise -resolves to the same factory function which -sqlite3Worker1Promiser() returns. - -If config is-a function or is an object which contains an onready -function, that function is replaced by a proxy which will resolve -after calling the original function and will reject if that -function throws. -*/ -globalThis.sqlite3Worker1Promiser.v2 = function callee(config = callee.defaultConfig) { - let oldFunc; - if ("function" == typeof config) { - oldFunc = config; - config = {}; - } else if ("function" === typeof config?.onready) { - oldFunc = config.onready; - delete config.onready; - } - const promiseProxy = Object.create(null); - config = Object.assign(config || Object.create(null), { onready: async function(func) { - try { - if (oldFunc) await oldFunc(func); - promiseProxy.resolve(func); - } catch (e) { - promiseProxy.reject(e); - } - } }); - const p = new Promise(function(resolve, reject) { - promiseProxy.resolve = resolve; - promiseProxy.reject = reject; - }); - try { - this.original(config); - } catch (e) { - promiseProxy.reject(e); - } - return p; -}.bind({ original: sqlite3Worker1Promiser }); -globalThis.sqlite3Worker1Promiser.v2.defaultConfig = globalThis.sqlite3Worker1Promiser.defaultConfig; -/** -When built as a module, we export sqlite3Worker1Promiser.v2() -instead of sqlite3Worker1Promise() because (A) its interface is more -conventional for ESM usage and (B) the ESM export option for this -API did not exist until v2 was created, so there's no backwards -incompatibility. -*/ -var sqlite3_worker1_promiser_default = sqlite3Worker1Promiser.v2; -delete globalThis.sqlite3Worker1Promiser; -//#endregion -//#region src/bin/sqlite3-bundler-friendly.mjs -/* @preserve -** -** LICENSE for the sqlite3 WebAssembly/JavaScript APIs. -** -** This bundle (typically released as sqlite3.js or sqlite3.mjs) -** is an amalgamation of JavaScript source code from two projects: -** -** 1) https://emscripten.org: the Emscripten "glue code" is covered by -** the terms of the MIT license and University of Illinois/NCSA -** Open Source License, as described at: -** -** https://emscripten.org/docs/introducing_emscripten/emscripten_license.html -** -** 2) https://sqlite.org: all code and documentation labeled as being -** from this source are released under the same terms as the sqlite3 -** C library: -** -** 2022-10-16 -** -** The author disclaims copyright to this source code. In place of a -** legal notice, here is a blessing: -** -** * May you do good and not evil. -** * May you find forgiveness for yourself and forgive others. -** * May you share freely, never taking more than you give. -*/ -/* @preserve -** This code was built from sqlite3 version... -** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" -** -** Emscripten SDK: 5.0.0 -*/ -async function sqlite3InitModule(moduleArg = {}) { - var moduleRtn; - var Module = moduleArg; - var ENVIRONMENT_IS_WEB = !!globalThis.window; - var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; - globalThis.process?.versions?.node && globalThis.process?.type; - var thisProgram = "./this.program"; - var _scriptName = import.meta.url; - 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) { - try { - scriptDirectory = new URL(".", _scriptName).href; - } catch {} - 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 = async (url) => { - var response = await fetch(url, { credentials: "same-origin" }); - if (response.ok) return response.arrayBuffer(); - throw new Error(response.status + " : " + response.url); - }; - } - var out = console.log.bind(console); - var err = console.error.bind(console); - var wasmBinary; - var ABORT = false, readyPromiseResolve, readyPromiseReject, HEAP8, HEAPU8, HEAP16, HEAP32, HEAPU32, HEAP64; - var runtimeInitialized = false; - function updateMemoryViews() { - var b = wasmMemory.buffer; - HEAP8 = new Int8Array(b); - HEAP16 = new Int16Array(b); - HEAPU8 = new Uint8Array(b); - new Uint16Array(b); - HEAP32 = new Int32Array(b); - HEAPU32 = new Uint32Array(b); - new Float32Array(b); - new Float64Array(b); - HEAP64 = new BigInt64Array(b); - new BigUint64Array(b); - } - function initMemory() { - if (Module["wasmMemory"]) wasmMemory = Module["wasmMemory"]; - else { - var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 8388608; - /** @suppress {checkTypes} */ - wasmMemory = new WebAssembly.Memory({ - "initial": INITIAL_MEMORY / 65536, - "maximum": 32768 - }); - } - updateMemoryViews(); - } - function preRun() { - if (Module["preRun"]) { - if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; - while (Module["preRun"].length) addOnPreRun(Module["preRun"].shift()); - } - callRuntimeCallbacks(onPreRuns); - } - function initRuntime() { - runtimeInitialized = true; - if (!Module["noFSInit"] && !FS.initialized) FS.init(); - TTY.init(); - wasmExports["__wasm_call_ctors"](); - FS.ignorePermissions = false; - } - function postRun() { - if (Module["postRun"]) { - if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; - while (Module["postRun"].length) addOnPostRun(Module["postRun"].shift()); - } - callRuntimeCallbacks(onPostRuns); - } - /** @param {string|number=} what */ - function abort(what) { - Module["onAbort"]?.(what); - what = "Aborted(" + what + ")"; - err(what); - ABORT = true; - what += ". Build with -sASSERTIONS for more info."; - /** @suppress {checkTypes} */ - var e = new WebAssembly.RuntimeError(what); - readyPromiseReject?.(e); - throw e; - } - var wasmBinaryFile; - function findWasmBinary() { - if (Module["locateFile"]) return locateFile("sqlite3.wasm"); - return new URL("sqlite3.wasm", import.meta.url).href; - } - 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"; - } - async function getWasmBinary(binaryFile) { - if (!wasmBinary) try { - var response = await readAsync(binaryFile); - return new Uint8Array(response); - } catch {} - return getBinarySync(binaryFile); - } - async function instantiateArrayBuffer(binaryFile, imports) { - try { - var binary = await getWasmBinary(binaryFile); - return await WebAssembly.instantiate(binary, imports); - } catch (reason) { - err(`failed to asynchronously prepare wasm: ${reason}`); - abort(reason); - } - } - async function instantiateAsync(binary, binaryFile, imports) { - if (!binary) try { - var response = fetch(binaryFile, { credentials: "same-origin" }); - return await WebAssembly.instantiateStreaming(response, imports); - } catch (reason) { - err(`wasm streaming compile failed: ${reason}`); - err("falling back to ArrayBuffer instantiation"); - } - return instantiateArrayBuffer(binaryFile, imports); - } - function getWasmImports() { - return { - "env": wasmImports, - "wasi_snapshot_preview1": wasmImports - }; - } - async function createWasm() { - /** @param {WebAssembly.Module=} module*/ - function receiveInstance(instance, module) { - wasmExports = instance.exports; - assignWasmExports(wasmExports); - return wasmExports; - } - function receiveInstantiationResult(result) { - return receiveInstance(result["instance"]); - } - var info = getWasmImports(); - if (Module["instantiateWasm"]) return new Promise((resolve, reject) => { - Module["instantiateWasm"](info, (inst, mod) => { - resolve(receiveInstance(inst, mod)); - }); - }); - wasmBinaryFile ??= findWasmBinary(); - return receiveInstantiationResult(await instantiateAsync(wasmBinary, wasmBinaryFile, info)); - } - var callRuntimeCallbacks = (callbacks) => { - while (callbacks.length > 0) callbacks.shift()(Module); - }; - var onPostRuns = []; - var addOnPostRun = (cb) => onPostRuns.push(cb); - var onPreRuns = []; - var addOnPreRun = (cb) => onPreRuns.push(cb); - var wasmMemory; - var PATH = { - isAbs: (path) => path.charAt(0) === "/", - splitPath: (filename) => { - return /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.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.slice(-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.slice(0, -1); - return root + dir; - }, - basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1], - join: (...paths) => PATH.normalize(paths.join("/")), - join2: (l, r) => PATH.normalize(l + "/" + r) - }; - var initRandomFill = () => { - return (view) => crypto.getRandomValues(view); - }; - 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).slice(1); - to = PATH_FS.resolve(to).slice(1); - function trim(arr) { - var start = 0; - for (; start < arr.length; start++) if (arr[start] !== "") break; - var end = arr.length - 1; - for (; end >= 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 < length; i++) if (fromParts[i] !== toParts[i]) { - samePartsLength = i; - break; - } - var outputParts = []; - for (var i = samePartsLength; i < fromParts.length; i++) outputParts.push(".."); - outputParts = outputParts.concat(toParts.slice(samePartsLength)); - return outputParts.join("/"); - } - }; - var UTF8Decoder = new TextDecoder(); - var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { - var maxIdx = idx + maxBytesToRead; - if (ignoreNul) return maxIdx; - while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; - return idx; - }; - /** - * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given - * array that contains uint8 values, returns a copy of that string as a - * Javascript String object. - * heapOrArray is either a regular array, or a JavaScript typed array view. - * @param {number=} idx - * @param {number=} maxBytesToRead - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ - var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { - var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); - return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr))); - }; - var FS_stdin_getChar_buffer = []; - var lengthBytesUTF8 = (str) => { - var len = 0; - for (var i = 0; i < str.length; ++i) { - var c = str.charCodeAt(i); - if (c <= 127) len++; - else if (c <= 2047) len += 2; - else if (c >= 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 < str.length; ++i) { - var u = str.codePointAt(i); - 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; - i++; - } - } - heap[outIdx] = 0; - return outIdx - startIdx; - }; - /** @type {function(string, boolean=, number=)} */ - var 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 (globalThis.window?.prompt) { - result = window.prompt("Input: "); - if (result !== null) result += "\n"; - } - 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; i < length; i++) { - var result; - try { - result = stream.tty.ops.get_char(stream.tty); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); - if (result === null || result === void 0) break; - bytesRead++; - buffer[offset + i] = result; - } - if (bytesRead) stream.node.atime = Date.now(); - return bytesRead; - }, - write(stream, buffer, offset, length, pos) { - if (!stream.tty || !stream.tty.ops.put_char) throw new FS.ErrnoError(60); - try { - for (var i = 0; i < length; i++) stream.tty.ops.put_char(stream.tty, buffer[offset + i]); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (length) stream.node.mtime = stream.node.ctime = Date.now(); - return i; - } - }, - default_tty_ops: { - get_char(tty) { - return FS_stdin_getChar(); - }, - put_char(tty, val) { - if (val === null || val === 10) { - out(UTF8ArrayToString(tty.output)); - tty.output = []; - } else if (val != 0) tty.output.push(val); - }, - fsync(tty) { - if (tty.output?.length > 0) { - out(UTF8ArrayToString(tty.output)); - 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)); - tty.output = []; - } else if (val != 0) tty.output.push(val); - }, - fsync(tty) { - if (tty.output?.length > 0) { - err(UTF8ArrayToString(tty.output)); - tty.output = []; - } - } - } - }; - var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); - var alignMemory = (size, alignment) => { - return Math.ceil(size / alignment) * alignment; - }; - var mmapAlloc = (size) => { - size = alignMemory(size, 65536); - var ptr = _emscripten_builtin_memalign(65536, size); - if (ptr) zeroMemory(ptr, size); - return ptr; - }; - var MEMFS = { - ops_table: null, - mount(mount) { - return MEMFS.createNode(null, "/", 16895, 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, - 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.atime = node.mtime = node.ctime = Date.now(); - if (parent) { - parent.contents[name] = node; - parent.atime = parent.mtime = parent.ctime = node.atime; - } - 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; - newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 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.atime); - attr.mtime = new Date(node.mtime); - attr.ctime = new Date(node.ctime); - attr.blksize = 4096; - attr.blocks = Math.ceil(attr.size / attr.blksize); - return attr; - }, - setattr(node, attr) { - for (const key of [ - "mode", - "atime", - "mtime", - "ctime" - ]) if (attr[key] != null) node[key] = attr[key]; - if (attr.size !== void 0) MEMFS.resizeFileStorage(node, attr.size); - }, - lookup(parent, name) { - if (!MEMFS.doesNotExistError) { - MEMFS.doesNotExistError = new FS.ErrnoError(44); - /** @suppress {checkTypes} */ - MEMFS.doesNotExistError.stack = ""; - } - throw MEMFS.doesNotExistError; - }, - mknod(parent, name, mode, dev) { - return MEMFS.createNode(parent, name, mode, dev); - }, - rename(old_node, new_dir, new_name) { - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) {} - if (new_node) { - if (FS.isDir(old_node.mode)) for (var i in new_node.contents) throw new FS.ErrnoError(55); - FS.hashRemoveNode(new_node); - } - delete old_node.parent.contents[old_node.name]; - new_dir.contents[new_name] = old_node; - old_node.name = new_name; - new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); - }, - unlink(parent, name) { - delete parent.contents[name]; - parent.ctime = parent.mtime = Date.now(); - }, - rmdir(parent, name) { - for (var i in FS.lookupNode(parent, name).contents) throw new FS.ErrnoError(55); - delete parent.contents[name]; - parent.ctime = parent.mtime = Date.now(); - }, - readdir(node) { - return [ - ".", - "..", - ...Object.keys(node.contents) - ]; - }, - symlink(parent, newname, oldpath) { - var node = MEMFS.createNode(parent, newname, 41471, 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; i < size; i++) buffer[offset + i] = contents[position + i]; - return size; - }, - write(stream, buffer, offset, length, position, canOwn) { - if (buffer.buffer === HEAP8.buffer) canOwn = false; - if (!length) return 0; - var node = stream.node; - node.mtime = node.ctime = Date.now(); - if (buffer.subarray && (!node.contents || node.contents.subarray)) { - if (canOwn) { - node.contents = buffer.subarray(offset, offset + length); - node.usedBytes = length; - return length; - } else if (node.usedBytes === 0 && position === 0) { - node.contents = buffer.slice(offset, offset + length); - node.usedBytes = length; - return length; - } else if (position + length <= node.usedBytes) { - node.contents.set(buffer.subarray(offset, offset + length), position); - return length; - } - } - MEMFS.expandFileStorage(node, position + length); - if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); - else for (var i = 0; i < length; i++) node.contents[position + i] = buffer[offset + i]; - node.usedBytes = Math.max(node.usedBytes, position + length); - return length; - }, - llseek(stream, offset, whence) { - var position = offset; - if (whence === 1) position += stream.position; - else if (whence === 2) { - if (FS.isFile(stream.node.mode)) position += stream.node.usedBytes; - } - if (position < 0) throw new FS.ErrnoError(28); - return position; - }, - mmap(stream, length, position, prot, flags) { - if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43); - var ptr; - var allocated; - var contents = stream.node.contents; - if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { - allocated = false; - ptr = contents.byteOffset; - } else { - allocated = true; - ptr = mmapAlloc(length); - if (!ptr) throw new FS.ErrnoError(48); - if (contents) { - if (position > 0 || position + length < contents.length) if (contents.subarray) contents = contents.subarray(position, position + length); - else contents = Array.prototype.slice.call(contents, position, position + length); - HEAP8.set(contents, ptr); - } - } - return { - ptr, - allocated - }; - }, - msync(stream, buffer, offset, length, mmapFlags) { - MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); - return 0; - } - } - }; - var FS_modeStringToFlags = (str) => { - var flags = { - "r": 0, - "r+": 2, - "w": 577, - "w+": 578, - "a": 1089, - "a+": 1090 - }[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 |= 365; - if (canWrite) mode |= 146; - return mode; - }; - var asyncLoad = async (url) => { - var arrayBuffer = await readAsync(url); - return new Uint8Array(arrayBuffer); - }; - var FS_createDataFile = (...args) => FS.createDataFile(...args); - var getUniqueRunDependency = (id) => { - return id; - }; - var runDependencies = 0; - var dependenciesFulfilled = null; - var removeRunDependency = (id) => { - runDependencies--; - Module["monitorRunDependencies"]?.(runDependencies); - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); - } - } - }; - var addRunDependency = (id) => { - runDependencies++; - Module["monitorRunDependencies"]?.(runDependencies); - }; - var preloadPlugins = []; - var FS_handledByPreloadPlugin = async (byteArray, fullname) => { - if (typeof Browser != "undefined") Browser.init(); - for (var plugin of preloadPlugins) if (plugin["canHandle"](fullname)) return plugin["handle"](byteArray, fullname); - return byteArray; - }; - var FS_preloadFile = async (parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish) => { - var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; - var dep = getUniqueRunDependency(`cp ${fullname}`); - addRunDependency(dep); - try { - var byteArray = url; - if (typeof url == "string") byteArray = await asyncLoad(url); - byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); - preFinish?.(); - if (!dontCreateFile) FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); - } finally { - removeRunDependency(dep); - } - }; - var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { - FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror); - }; - var FS = { - root: null, - mounts: [], - devices: {}, - streams: [], - nextInode: 1, - nameTable: null, - currentPath: "/", - initialized: false, - ignorePermissions: true, - filesystems: null, - syncFSRequests: 0, - readFiles: {}, - ErrnoError: class { - name = "ErrnoError"; - constructor(errno) { - this.errno = errno; - } - }, - FSStream: class { - 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 { - node_ops = {}; - stream_ops = {}; - readMode = 365; - writeMode = 146; - mounted = null; - constructor(parent, name, mode, rdev) { - if (!parent) parent = this; - this.parent = parent; - this.mount = parent.mount; - this.id = FS.nextInode++; - this.name = name; - this.mode = mode; - this.rdev = rdev; - this.atime = this.mtime = this.ctime = Date.now(); - } - 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 = {}) { - if (!path) throw new FS.ErrnoError(44); - opts.follow_mount ??= true; - if (!PATH.isAbs(path)) path = FS.cwd() + "/" + path; - linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { - var parts = path.split("/").filter((p) => !!p); - var current = FS.root; - var current_path = "/"; - for (var i = 0; i < parts.length; i++) { - var islast = i === parts.length - 1; - if (islast && opts.parent) break; - if (parts[i] === ".") continue; - if (parts[i] === "..") { - current_path = PATH.dirname(current_path); - if (FS.isRoot(current)) { - path = current_path + "/" + parts.slice(i + 1).join("/"); - nlinks--; - continue linkloop; - } else current = current.parent; - continue; - } - current_path = PATH.join2(current_path, parts[i]); - try { - current = FS.lookupNode(current, parts[i]); - } catch (e) { - if (e?.errno === 44 && islast && opts.noent_okay) return { path: current_path }; - throw e; - } - if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) current = current.mounted.root; - if (FS.isLink(current.mode) && (!islast || opts.follow)) { - if (!current.node_ops.readlink) throw new FS.ErrnoError(52); - var link = current.node_ops.readlink(current); - if (!PATH.isAbs(link)) link = PATH.dirname(current_path) + "/" + link; - path = link + "/" + parts.slice(i + 1).join("/"); - continue linkloop; - } - } - return { - path: current_path, - node: current - }; - } - throw new FS.ErrnoError(32); - }, - 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 < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0; - return (parentid + hash >>> 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) { - if (!FS.isDir(dir.mode)) return 54; - try { - 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 & 576) return 31; - } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); - }, - checkOpExists(op, err) { - if (!op) throw new FS.ErrnoError(err); - return op; - }, - 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; - }, - doSetAttr(stream, node, attr) { - var setattr = stream?.stream_ops.setattr; - var arg = setattr ? stream : node; - setattr ??= node.node_ops.setattr; - FS.checkOpExists(setattr, 63); - setattr(arg, attr); - }, - chrdev_stream_ops: { - open(stream) { - stream.stream_ops = FS.getDevice(stream.node.rdev).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); - } - for (var mount of mounts) if (mount.type.syncfs) mount.type.syncfs(mount, populate, done); - else done(null); - }, - 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); - for (var [hash, current] of Object.entries(FS.nameTable)) 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 parent = FS.lookupPath(path, { parent: true }).node; - var name = PATH.basename(path); - if (!name) throw new FS.ErrnoError(28); - if (name === "." || name === "..") throw new FS.ErrnoError(20); - 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); - }, - statfs(path) { - return FS.statfsNode(FS.lookupPath(path, { follow: true }).node); - }, - statfsStream(stream) { - return FS.statfsNode(stream.node); - }, - statfsNode(node) { - var rtn = { - bsize: 4096, - frsize: 4096, - blocks: 1e6, - bfree: 5e5, - bavail: 5e5, - files: FS.nextInode, - ffree: FS.nextInode - 1, - fsid: 42, - flags: 2, - namelen: 255 - }; - if (node.node_ops.statfs) Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root)); - return rtn; - }, - create(path, mode = 438) { - mode &= 4095; - mode |= 32768; - return FS.mknod(path, mode, 0); - }, - mkdir(path, mode = 511) { - mode &= 1023; - mode |= 16384; - return FS.mknod(path, mode, 0); - }, - mkdirTree(path, mode) { - var dirs = path.split("/"); - var d = ""; - for (var dir of dirs) { - if (!dir) continue; - if (d || PATH.isAbs(path)) d += "/"; - d += dir; - try { - FS.mkdir(d, mode); - } catch (e) { - if (e.errno != 20) throw e; - } - } - }, - mkdev(path, mode, dev) { - if (typeof dev == "undefined") { - dev = mode; - mode = 438; - } - mode |= 8192; - return FS.mknod(path, mode, dev); - }, - symlink(oldpath, newpath) { - if (!PATH_FS.resolve(oldpath)) throw new FS.ErrnoError(44); - var parent = FS.lookupPath(newpath, { parent: true }).node; - if (!parent) throw new FS.ErrnoError(44); - var newname = PATH.basename(newpath); - var errCode = FS.mayCreate(parent, newname); - if (errCode) throw new FS.ErrnoError(errCode); - if (!parent.node_ops.symlink) throw new FS.ErrnoError(63); - return parent.node_ops.symlink(parent, newname, oldpath); - }, - rename(old_path, new_path) { - var old_dirname = PATH.dirname(old_path); - var new_dirname = PATH.dirname(new_path); - var old_name = PATH.basename(old_path); - var new_name = PATH.basename(new_path); - var lookup = FS.lookupPath(old_path, { parent: true }), old_dir = lookup.node, new_dir; - lookup = FS.lookupPath(new_path, { parent: true }); - new_dir = lookup.node; - if (!old_dir || !new_dir) throw new FS.ErrnoError(44); - if (old_dir.mount !== new_dir.mount) throw new FS.ErrnoError(75); - var old_node = FS.lookupNode(old_dir, old_name); - var relative = PATH_FS.relative(old_path, new_dirname); - if (relative.charAt(0) !== ".") throw new FS.ErrnoError(28); - relative = PATH_FS.relative(new_path, old_dirname); - if (relative.charAt(0) !== ".") throw new FS.ErrnoError(55); - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) {} - if (old_node === new_node) return; - var isdir = FS.isDir(old_node.mode); - var errCode = FS.mayDelete(old_dir, old_name, isdir); - if (errCode) throw new FS.ErrnoError(errCode); - errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); - if (errCode) throw new FS.ErrnoError(errCode); - if (!old_dir.node_ops.rename) throw new FS.ErrnoError(63); - if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) throw new FS.ErrnoError(10); - if (new_dir !== old_dir) { - errCode = FS.nodePermissions(old_dir, "w"); - if (errCode) throw new FS.ErrnoError(errCode); - } - FS.hashRemoveNode(old_node); - try { - old_dir.node_ops.rename(old_node, new_dir, new_name); - old_node.parent = new_dir; - } catch (e) { - throw e; - } finally { - FS.hashAddNode(old_node); - } - }, - rmdir(path) { - var parent = FS.lookupPath(path, { parent: true }).node; - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var errCode = FS.mayDelete(parent, name, true); - if (errCode) throw new FS.ErrnoError(errCode); - if (!parent.node_ops.rmdir) throw new FS.ErrnoError(63); - if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); - parent.node_ops.rmdir(parent, name); - FS.destroyNode(node); - }, - readdir(path) { - var node = FS.lookupPath(path, { follow: true }).node; - return FS.checkOpExists(node.node_ops.readdir, 54)(node); - }, - unlink(path) { - var parent = FS.lookupPath(path, { parent: true }).node; - if (!parent) throw new FS.ErrnoError(44); - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var errCode = FS.mayDelete(parent, name, false); - if (errCode) throw new FS.ErrnoError(errCode); - if (!parent.node_ops.unlink) throw new FS.ErrnoError(63); - if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); - parent.node_ops.unlink(parent, name); - FS.destroyNode(node); - }, - readlink(path) { - var link = FS.lookupPath(path).node; - if (!link) throw new FS.ErrnoError(44); - if (!link.node_ops.readlink) throw new FS.ErrnoError(28); - return link.node_ops.readlink(link); - }, - stat(path, dontFollow) { - var node = FS.lookupPath(path, { follow: !dontFollow }).node; - return FS.checkOpExists(node.node_ops.getattr, 63)(node); - }, - fstat(fd) { - var stream = FS.getStreamChecked(fd); - var node = stream.node; - var getattr = stream.stream_ops.getattr; - var arg = getattr ? stream : node; - getattr ??= node.node_ops.getattr; - FS.checkOpExists(getattr, 63); - return getattr(arg); - }, - lstat(path) { - return FS.stat(path, true); - }, - doChmod(stream, node, mode, dontFollow) { - FS.doSetAttr(stream, node, { - mode: mode & 4095 | node.mode & -4096, - ctime: Date.now(), - dontFollow - }); - }, - chmod(path, mode, dontFollow) { - var node; - if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; - else node = path; - FS.doChmod(null, node, mode, dontFollow); - }, - lchmod(path, mode) { - FS.chmod(path, mode, true); - }, - fchmod(fd, mode) { - var stream = FS.getStreamChecked(fd); - FS.doChmod(stream, stream.node, mode, false); - }, - doChown(stream, node, dontFollow) { - FS.doSetAttr(stream, node, { - timestamp: Date.now(), - dontFollow - }); - }, - chown(path, uid, gid, dontFollow) { - var node; - if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; - else node = path; - FS.doChown(null, node, dontFollow); - }, - lchown(path, uid, gid) { - FS.chown(path, uid, gid, true); - }, - fchown(fd, uid, gid) { - var stream = FS.getStreamChecked(fd); - FS.doChown(stream, stream.node, false); - }, - doTruncate(stream, node, len) { - if (FS.isDir(node.mode)) throw new FS.ErrnoError(31); - if (!FS.isFile(node.mode)) throw new FS.ErrnoError(28); - var errCode = FS.nodePermissions(node, "w"); - if (errCode) throw new FS.ErrnoError(errCode); - FS.doSetAttr(stream, node, { - size: len, - timestamp: Date.now() - }); - }, - truncate(path, len) { - if (len < 0) throw new FS.ErrnoError(28); - var node; - if (typeof path == "string") node = FS.lookupPath(path, { follow: true }).node; - else node = path; - FS.doTruncate(null, node, len); - }, - ftruncate(fd, len) { - var stream = FS.getStreamChecked(fd); - if (len < 0 || (stream.flags & 2097155) === 0) throw new FS.ErrnoError(28); - FS.doTruncate(stream, stream.node, len); - }, - utime(path, atime, mtime) { - var node = FS.lookupPath(path, { follow: true }).node; - FS.checkOpExists(node.node_ops.setattr, 63)(node, { - atime, - mtime - }); - }, - open(path, flags, mode = 438) { - if (path === "") throw new FS.ErrnoError(44); - flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; - if (flags & 64) mode = mode & 4095 | 32768; - else mode = 0; - var node; - var isDirPath; - if (typeof path == "object") node = path; - else { - isDirPath = path.endsWith("/"); - var lookup = FS.lookupPath(path, { - follow: !(flags & 131072), - noent_okay: true - }); - node = lookup.node; - path = lookup.path; - } - var created = false; - if (flags & 64) if (node) { - if (flags & 128) throw new FS.ErrnoError(20); - } else if (isDirPath) throw new FS.ErrnoError(31); - else { - node = FS.mknod(path, mode | 511, 0); - created = true; - } - if (!node) throw new FS.ErrnoError(44); - if (FS.isChrdev(node.mode)) flags &= -513; - if (flags & 65536 && !FS.isDir(node.mode)) throw new FS.ErrnoError(54); - if (!created) { - var errCode = FS.mayOpen(node, flags); - if (errCode) throw new FS.ErrnoError(errCode); - } - if (flags & 512 && !created) FS.truncate(node, 0); - flags &= -131713; - var stream = FS.createStream({ - node, - path: FS.getPath(node), - flags, - seekable: true, - position: 0, - stream_ops: node.stream_ops, - ungotten: [], - error: false - }); - if (stream.stream_ops.open) stream.stream_ops.open(stream); - if (created) FS.chmod(node, mode & 511); - if (Module["logReadFiles"] && !(flags & 1)) { - if (!(path in FS.readFiles)) FS.readFiles[path] = 1; - } - return stream; - }, - close(stream) { - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if (stream.getdents) stream.getdents = null; - try { - if (stream.stream_ops.close) stream.stream_ops.close(stream); - } catch (e) { - throw e; - } finally { - FS.closeStream(stream.fd); - } - stream.fd = null; - }, - isClosed(stream) { - return stream.fd === null; - }, - llseek(stream, offset, whence) { - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if (!stream.seekable || !stream.stream_ops.llseek) throw new FS.ErrnoError(70); - if (whence != 0 && whence != 1 && whence != 2) throw new FS.ErrnoError(28); - stream.position = stream.stream_ops.llseek(stream, offset, whence); - stream.ungotten = []; - return stream.position; - }, - read(stream, buffer, offset, length, position) { - if (length < 0 || position < 0) throw new FS.ErrnoError(28); - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(8); - if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); - if (!stream.stream_ops.read) throw new FS.ErrnoError(28); - var seeking = typeof position != "undefined"; - if (!seeking) position = stream.position; - else if (!stream.seekable) throw new FS.ErrnoError(70); - var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); - if (!seeking) stream.position += bytesRead; - return bytesRead; - }, - write(stream, buffer, offset, length, position, canOwn) { - if (length < 0 || position < 0) throw new FS.ErrnoError(28); - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if ((stream.flags & 2097155) === 0) throw new FS.ErrnoError(8); - if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); - if (!stream.stream_ops.write) throw new FS.ErrnoError(28); - if (stream.seekable && stream.flags & 1024) FS.llseek(stream, 0, 2); - var seeking = typeof position != "undefined"; - if (!seeking) position = stream.position; - else if (!stream.seekable) throw new FS.ErrnoError(70); - var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); - if (!seeking) stream.position += bytesWritten; - return bytesWritten; - }, - mmap(stream, length, position, prot, flags) { - if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) throw new FS.ErrnoError(2); - if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(2); - if (!stream.stream_ops.mmap) throw new FS.ErrnoError(43); - if (!length) throw new FS.ErrnoError(28); - return stream.stream_ops.mmap(stream, length, position, prot, flags); - }, - msync(stream, buffer, offset, length, mmapFlags) { - if (!stream.stream_ops.msync) return 0; - return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); - }, - ioctl(stream, cmd, arg) { - if (!stream.stream_ops.ioctl) throw new FS.ErrnoError(59); - return stream.stream_ops.ioctl(stream, cmd, arg); - }, - readFile(path, opts = {}) { - opts.flags = opts.flags || 0; - opts.encoding = opts.encoding || "binary"; - if (opts.encoding !== "utf8" && opts.encoding !== "binary") abort(`Invalid encoding type "${opts.encoding}"`); - var stream = FS.open(path, opts.flags); - var length = FS.stat(path).size; - var buf = new Uint8Array(length); - FS.read(stream, buf, 0, length, 0); - if (opts.encoding === "utf8") buf = UTF8ArrayToString(buf); - FS.close(stream); - return buf; - }, - writeFile(path, data, opts = {}) { - opts.flags = opts.flags || 577; - var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data == "string") data = new Uint8Array(intArrayFromString(data, true)); - if (ArrayBuffer.isView(data)) FS.write(stream, data, 0, data.byteLength, void 0, opts.canOwn); - else abort("Unsupported data type"); - FS.close(stream); - }, - cwd: () => FS.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, - llseek: () => 0 - }); - 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) { - randomFill(randomBuffer); - randomLeft = 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", 16895, 73); - node.stream_ops = { llseek: MEMFS.stream_ops.llseek }; - 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 }, - id: fd + 1 - }; - ret.parent = ret; - return ret; - }, - readdir() { - return Array.from(FS.streams.entries()).filter(([k, v]) => v).map(([k, v]) => k.toString()); - } - }; - 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"); - FS.open("/dev/stdin", 0); - FS.open("/dev/stdout", 1); - FS.open("/dev/stderr", 1); - }, - staticInit() { - FS.nameTable = new Array(4096); - FS.mount(MEMFS, {}, "/"); - FS.createDefaultDirectories(); - FS.createDefaultDevices(); - FS.createSpecialDirectories(); - FS.filesystems = { "MEMFS": 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 stream of FS.streams) if (stream) FS.close(stream); - }, - findObject(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (!ret.exists) return null; - return ret.object; - }, - analyzePath(path, dontResolveLastLink) { - try { - var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - path = lookup.path; - } catch (e) {} - var ret = { - isRoot: false, - exists: false, - error: 0, - name: null, - path: null, - object: null, - parentExists: false, - parentPath: null, - parentObject: null - }; - try { - var lookup = FS.lookupPath(path, { parent: true }); - ret.parentExists = true; - ret.parentPath = lookup.path; - ret.parentObject = lookup.node; - ret.name = PATH.basename(path); - lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - ret.exists = true; - ret.path = lookup.path; - ret.object = lookup.node; - ret.name = lookup.node.name; - ret.isRoot = lookup.path === "/"; - } catch (e) { - ret.error = e.errno; - } - return ret; - }, - createPath(parent, path, canRead, canWrite) { - parent = typeof parent == "string" ? parent : FS.getPath(parent); - var parts = path.split("/").reverse(); - while (parts.length) { - var part = parts.pop(); - if (!part) continue; - var current = PATH.join2(parent, part); - try { - FS.mkdir(current); - } catch (e) { - if (e.errno != 20) throw e; - } - parent = current; - } - return current; - }, - createFile(parent, name, properties, canRead, canWrite) { - var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); - var mode = FS_getMode(canRead, canWrite); - return FS.create(path, mode); - }, - createDataFile(parent, name, data, canRead, canWrite, canOwn) { - var path = name; - if (parent) { - parent = typeof parent == "string" ? parent : FS.getPath(parent); - path = name ? PATH.join2(parent, name) : parent; - } - var mode = FS_getMode(canRead, canWrite); - var node = FS.create(path, mode); - if (data) { - if (typeof data == "string") { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } - FS.chmod(node, mode | 146); - var stream = FS.open(node, 577); - FS.write(stream, data, 0, data.length, 0, canOwn); - FS.close(stream); - FS.chmod(node, mode); - } - }, - createDevice(parent, name, input, output) { - var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); - var mode = FS_getMode(!!input, !!output); - FS.createDevice.major ??= 64; - var dev = FS.makedev(FS.createDevice.major++, 0); - FS.registerDevice(dev, { - open(stream) { - stream.seekable = false; - }, - close(stream) { - if (output?.buffer?.length) output(10); - }, - read(stream, buffer, offset, length, pos) { - var bytesRead = 0; - for (var i = 0; i < length; i++) { - var result; - try { - result = input(); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); - if (result === null || result === void 0) break; - bytesRead++; - buffer[offset + i] = result; - } - if (bytesRead) stream.node.atime = Date.now(); - return bytesRead; - }, - write(stream, buffer, offset, length, pos) { - for (var i = 0; i < length; i++) try { - output(buffer[offset + i]); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (length) stream.node.mtime = stream.node.ctime = Date.now(); - return i; - } - }); - return FS.mkdev(path, mode, dev); - }, - forceLoadFile(obj) { - if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; - if (globalThis.XMLHttpRequest) abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - else try { - obj.contents = readBinary(obj.url); - } catch (e) { - throw new FS.ErrnoError(29); - } - }, - createLazyFile(parent, name, url, canRead, canWrite) { - class LazyUint8Array { - lengthKnown = false; - chunks = []; - get(idx) { - if (idx > this.length - 1 || idx < 0) return; - 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)) abort("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) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength - 1) abort("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)) abort("Couldn't load " + url + ". Status: " + xhr.status); - if (xhr.response !== void 0) 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") abort("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 (globalThis.XMLHttpRequest) { - if (!ENVIRONMENT_IS_WORKER) abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"); - var properties = { - isDevice: false, - contents: new LazyUint8Array() - }; - } 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 = {}; - for (const [key, fn] of Object.entries(node.stream_ops)) 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 < size; i++) buffer[offset + i] = contents[position + i]; - else for (var i = 0; i < size; i++) buffer[offset + i] = contents.get(position + i); - return size; - } - stream_ops.read = (stream, buffer, offset, length, position) => { - 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; - } - }; - /** - * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the - * emscripten HEAP, returns a copy of that string as a Javascript String object. - * - * @param {number} ptr - * @param {number=} maxBytesToRead - An optional length that specifies the - * maximum number of bytes to read. You can omit this parameter to scan the - * string until the first 0 byte. If maxBytesToRead is passed, and the string - * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the - * string will cut short at that byte index. - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ - var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { - if (!ptr) return ""; - var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); - return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); - }; - var SYSCALLS = { - calculateAt(dirfd, path, allowEmpty) { - if (PATH.isAbs(path)) return path; - var dir; - if (dirfd === -100) dir = FS.cwd(); - else dir = SYSCALLS.getStreamFromFD(dirfd).path; - if (path.length == 0) { - if (!allowEmpty) throw new FS.ErrnoError(44); - return dir; - } - return dir + "/" + path; - }, - writeStat(buf, stat) { - HEAPU32[buf >> 2] = stat.dev; - HEAPU32[buf + 4 >> 2] = stat.mode; - HEAPU32[buf + 8 >> 2] = stat.nlink; - HEAPU32[buf + 12 >> 2] = stat.uid; - HEAPU32[buf + 16 >> 2] = stat.gid; - HEAPU32[buf + 20 >> 2] = stat.rdev; - HEAP64[buf + 24 >> 3] = BigInt(stat.size); - 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(); - HEAP64[buf + 40 >> 3] = BigInt(Math.floor(atime / 1e3)); - HEAPU32[buf + 48 >> 2] = atime % 1e3 * 1e3 * 1e3; - HEAP64[buf + 56 >> 3] = BigInt(Math.floor(mtime / 1e3)); - HEAPU32[buf + 64 >> 2] = mtime % 1e3 * 1e3 * 1e3; - HEAP64[buf + 72 >> 3] = BigInt(Math.floor(ctime / 1e3)); - HEAPU32[buf + 80 >> 2] = ctime % 1e3 * 1e3 * 1e3; - HEAP64[buf + 88 >> 3] = BigInt(stat.ino); - return 0; - }, - writeStatFs(buf, stats) { - HEAPU32[buf + 4 >> 2] = stats.bsize; - HEAPU32[buf + 60 >> 2] = stats.bsize; - HEAP64[buf + 8 >> 3] = BigInt(stats.blocks); - HEAP64[buf + 16 >> 3] = BigInt(stats.bfree); - HEAP64[buf + 24 >> 3] = BigInt(stats.bavail); - HEAP64[buf + 32 >> 3] = BigInt(stats.files); - HEAP64[buf + 40 >> 3] = BigInt(stats.ffree); - HEAPU32[buf + 48 >> 2] = stats.fsid; - HEAPU32[buf + 64 >> 2] = stats.flags; - HEAPU32[buf + 56 >> 2] = stats.namelen; - }, - 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) { - return FS.getStreamChecked(fd); - }, - varargs: void 0, - getStr(ptr) { - return UTF8ToString(ptr); - } - }; - 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 & -8) return -28; - var node = FS.lookupPath(path, { follow: true }).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; - } - } - var 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++; - return FS.dupStream(stream, arg).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 { - return SYSCALLS.writeStat(buf, FS.fstat(fd)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - var INT53_MAX = 9007199254740992; - var INT53_MIN = -9007199254740992; - var bigintToI53Checked = (num) => num < INT53_MIN || num > INT53_MAX ? NaN : Number(num); - function ___syscall_ftruncate64(fd, length) { - length = bigintToI53Checked(length); - 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) => { - return 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 (size < cwdLengthInBytes) return -68; - stringToUTF8(cwd, buf, size); - return cwdLengthInBytes; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_ioctl(fd, op, varargs) { - SYSCALLS.varargs = varargs; - try { - var stream = SYSCALLS.getStreamFromFD(fd); - switch (op) { - case 21509: - if (!stream.tty) return -59; - return 0; - case 21505: - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tcgets) { - var termios = stream.tty.ops.ioctl_tcgets(stream); - var argp = syscallGetVarargP(); - HEAP32[argp >> 2] = termios.c_iflag || 0; - HEAP32[argp + 4 >> 2] = termios.c_oflag || 0; - HEAP32[argp + 8 >> 2] = termios.c_cflag || 0; - HEAP32[argp + 12 >> 2] = termios.c_lflag || 0; - for (var i = 0; i < 32; i++) HEAP8[argp + i + 17] = termios.c_cc[i] || 0; - return 0; - } - return 0; - case 21510: - case 21511: - case 21512: - if (!stream.tty) return -59; - return 0; - case 21506: - case 21507: - case 21508: - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tcsets) { - var argp = syscallGetVarargP(); - var c_iflag = HEAP32[argp >> 2]; - var c_oflag = HEAP32[argp + 4 >> 2]; - var c_cflag = HEAP32[argp + 8 >> 2]; - var c_lflag = HEAP32[argp + 12 >> 2]; - var c_cc = []; - for (var i = 0; i < 32; i++) c_cc.push(HEAP8[argp + i + 17]); - return stream.tty.ops.ioctl_tcsets(stream.tty, op, { - c_iflag, - c_oflag, - c_cflag, - c_lflag, - c_cc - }); - } - return 0; - case 21519: - if (!stream.tty) return -59; - var argp = syscallGetVarargP(); - HEAP32[argp >> 2] = 0; - return 0; - case 21520: - if (!stream.tty) return -59; - return -28; - case 21537: - case 21531: - var argp = syscallGetVarargP(); - return FS.ioctl(stream, op, argp); - case 21523: - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tiocgwinsz) { - var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); - var argp = syscallGetVarargP(); - HEAP16[argp >> 1] = winsize[0]; - HEAP16[argp + 2 >> 1] = winsize[1]; - } - return 0; - case 21524: - if (!stream.tty) return -59; - return 0; - case 21515: - if (!stream.tty) return -59; - return 0; - default: return -28; - } - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_lstat64(path, buf) { - try { - path = SYSCALLS.getStr(path); - return SYSCALLS.writeStat(buf, FS.lstat(path)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_mkdirat(dirfd, path, mode) { - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - FS.mkdir(path, mode, 0); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_newfstatat(dirfd, path, buf, flags) { - try { - path = SYSCALLS.getStr(path); - var nofollow = flags & 256; - var allowEmpty = flags & 4096; - flags = flags & -6401; - path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); - return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_openat(dirfd, path, flags, varargs) { - SYSCALLS.varargs = varargs; - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - var mode = varargs ? syscallGetVarargI() : 0; - return FS.open(path, flags, mode).fd; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_readlinkat(dirfd, path, buf, bufsize) { - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (bufsize <= 0) return -28; - var ret = FS.readlink(path); - var len = Math.min(bufsize, lengthBytesUTF8(ret)); - var endChar = HEAP8[buf + len]; - stringToUTF8(ret, buf, bufsize + 1); - HEAP8[buf + len] = endChar; - return len; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_rmdir(path) { - try { - path = SYSCALLS.getStr(path); - FS.rmdir(path); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_stat64(path, buf) { - try { - path = SYSCALLS.getStr(path); - return SYSCALLS.writeStat(buf, FS.stat(path)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_unlinkat(dirfd, path, flags) { - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (!flags) FS.unlink(path); - else if (flags === 512) FS.rmdir(path); - else return -28; - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - var readI53FromI64 = (ptr) => { - return HEAPU32[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 = null; - 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 = null; - else mtime = seconds * 1e3 + nanoseconds / (1e3 * 1e3); - } - if ((mtime ?? atime) !== null) FS.utime(path, atime, mtime); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - 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) => { - return (isLeapYear(date.getFullYear()) ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE)[date.getMonth()] + date.getDate() - 1; - }; - function __localtime_js(time, tmPtr) { - time = bigintToI53Checked(time); - var date = /* @__PURE__ */ 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, allocated, addr) { - offset = bigintToI53Checked(offset); - try { - 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) { - offset = bigintToI53Checked(offset); - 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 __tzset_js = (timezone, daylight, std_name, dst_name) => { - var currentYear = (/* @__PURE__ */ 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); - return `UTC${sign}${String(Math.floor(absOffset / 60)).padStart(2, "0")}${String(absOffset % 60).padStart(2, "0")}`; - }; - var winterName = extractZone(winterOffset); - var summerName = extractZone(summerOffset); - if (summerOffset < winterOffset) { - stringToUTF8(winterName, std_name, 17); - stringToUTF8(summerName, dst_name, 17); - } else { - stringToUTF8(winterName, dst_name, 17); - stringToUTF8(summerName, std_name, 17); - } - }; - var _emscripten_get_now = () => performance.now(); - var _emscripten_date_now = () => Date.now(); - var nowIsMonotonic = 1; - var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; - function _clock_time_get(clk_id, ignored_precision, ptime) { - ignored_precision = bigintToI53Checked(ignored_precision); - if (!checkWasiClock(clk_id)) return 28; - var now; - if (clk_id === 0) now = _emscripten_date_now(); - else if (nowIsMonotonic) now = _emscripten_get_now(); - else return 52; - var nsec = Math.round(now * 1e3 * 1e3); - HEAP64[ptime >> 3] = BigInt(nsec); - return 0; - } - var getHeapMax = () => 2147483648; - var _emscripten_get_heap_max = () => getHeapMax(); - var growMemory = (size) => { - var pages = (size - wasmMemory.buffer.byteLength + 65535) / 65536 | 0; - 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); - if (growMemory(Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)))) return true; - } - return false; - }; - var ENV = {}; - var getExecutableName = () => thisProgram || "./this.program"; - var getEnvStrings = () => { - if (!getEnvStrings.strings) { - var lang = (globalThis.navigator?.language ?? "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] === void 0) 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 _environ_get = (__environ, environ_buf) => { - var bufSize = 0; - var envp = 0; - for (var string of getEnvStrings()) { - var ptr = environ_buf + bufSize; - HEAPU32[__environ + envp >> 2] = ptr; - bufSize += stringToUTF8(string, ptr, Infinity) + 1; - envp += 4; - } - return 0; - }; - var _environ_sizes_get = (penviron_count, penviron_buf_size) => { - var strings = getEnvStrings(); - HEAPU32[penviron_count >> 2] = strings.length; - var bufSize = 0; - for (var string of strings) bufSize += lengthBytesUTF8(string) + 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; - HEAP64[pbuf + 8 >> 3] = BigInt(rightsBase); - HEAP64[pbuf + 16 >> 3] = BigInt(rightsInheriting); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - /** @param {number=} offset */ - var doReadv = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAPU32[iov >> 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 < len) break; - if (typeof offset != "undefined") offset += curr; - } - return ret; - }; - function _fd_read(fd, iov, iovcnt, pnum) { - try { - var num = doReadv(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); - HEAPU32[pnum >> 2] = num; - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - function _fd_seek(fd, offset, whence, newOffset) { - offset = bigintToI53Checked(offset); - try { - if (isNaN(offset)) return 61; - var stream = SYSCALLS.getStreamFromFD(fd); - FS.llseek(stream, offset, whence); - HEAP64[newOffset >> 3] = BigInt(stream.position); - 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); - return stream.stream_ops?.fsync?.(stream); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - /** @param {number=} offset */ - var doWritev = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAPU32[iov >> 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 < len) break; - if (typeof offset != "undefined") offset += curr; - } - return ret; - }; - function _fd_write(fd, iov, iovcnt, pnum) { - try { - var num = doWritev(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); - HEAPU32[pnum >> 2] = num; - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - FS.createPreloadedFile = FS_createPreloadedFile; - FS.preloadFile = FS_preloadFile; - FS.staticInit(); - initMemory(); - if (Module["noExitRuntime"]) Module["noExitRuntime"]; - if (Module["preloadPlugins"]) preloadPlugins = Module["preloadPlugins"]; - if (Module["print"]) out = Module["print"]; - if (Module["printErr"]) err = Module["printErr"]; - if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; - if (Module["arguments"]) Module["arguments"]; - if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; - if (Module["preInit"]) { - if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; - while (Module["preInit"].length > 0) Module["preInit"].shift()(); - } - Module["wasmMemory"] = wasmMemory; - var _emscripten_builtin_memalign; - function assignWasmExports(wasmExports) { - Module["_sqlite3_status64"] = wasmExports["sqlite3_status64"]; - Module["_sqlite3_status"] = wasmExports["sqlite3_status"]; - Module["_sqlite3_db_status64"] = wasmExports["sqlite3_db_status64"]; - Module["_sqlite3_msize"] = wasmExports["sqlite3_msize"]; - Module["_sqlite3_db_status"] = wasmExports["sqlite3_db_status"]; - Module["_sqlite3_vfs_find"] = wasmExports["sqlite3_vfs_find"]; - Module["_sqlite3_initialize"] = wasmExports["sqlite3_initialize"]; - Module["_sqlite3_malloc"] = wasmExports["sqlite3_malloc"]; - Module["_sqlite3_free"] = wasmExports["sqlite3_free"]; - Module["_sqlite3_vfs_register"] = wasmExports["sqlite3_vfs_register"]; - Module["_sqlite3_vfs_unregister"] = wasmExports["sqlite3_vfs_unregister"]; - Module["_sqlite3_malloc64"] = wasmExports["sqlite3_malloc64"]; - Module["_sqlite3_realloc"] = wasmExports["sqlite3_realloc"]; - Module["_sqlite3_realloc64"] = wasmExports["sqlite3_realloc64"]; - Module["_sqlite3_value_text"] = wasmExports["sqlite3_value_text"]; - Module["_sqlite3_randomness"] = wasmExports["sqlite3_randomness"]; - Module["_sqlite3_stricmp"] = wasmExports["sqlite3_stricmp"]; - Module["_sqlite3_strnicmp"] = wasmExports["sqlite3_strnicmp"]; - Module["_sqlite3_uri_parameter"] = wasmExports["sqlite3_uri_parameter"]; - Module["_sqlite3_uri_boolean"] = wasmExports["sqlite3_uri_boolean"]; - Module["_sqlite3_serialize"] = wasmExports["sqlite3_serialize"]; - Module["_sqlite3_prepare_v2"] = wasmExports["sqlite3_prepare_v2"]; - Module["_sqlite3_step"] = wasmExports["sqlite3_step"]; - Module["_sqlite3_column_int64"] = wasmExports["sqlite3_column_int64"]; - Module["_sqlite3_reset"] = wasmExports["sqlite3_reset"]; - Module["_sqlite3_exec"] = wasmExports["sqlite3_exec"]; - Module["_sqlite3_column_int"] = wasmExports["sqlite3_column_int"]; - Module["_sqlite3_finalize"] = wasmExports["sqlite3_finalize"]; - Module["_sqlite3_file_control"] = wasmExports["sqlite3_file_control"]; - Module["_sqlite3_column_name"] = wasmExports["sqlite3_column_name"]; - Module["_sqlite3_column_text"] = wasmExports["sqlite3_column_text"]; - Module["_sqlite3_column_type"] = wasmExports["sqlite3_column_type"]; - Module["_sqlite3_errmsg"] = wasmExports["sqlite3_errmsg"]; - Module["_sqlite3_deserialize"] = wasmExports["sqlite3_deserialize"]; - Module["_sqlite3_clear_bindings"] = wasmExports["sqlite3_clear_bindings"]; - Module["_sqlite3_value_blob"] = wasmExports["sqlite3_value_blob"]; - Module["_sqlite3_value_bytes"] = wasmExports["sqlite3_value_bytes"]; - Module["_sqlite3_value_double"] = wasmExports["sqlite3_value_double"]; - Module["_sqlite3_value_int"] = wasmExports["sqlite3_value_int"]; - Module["_sqlite3_value_int64"] = wasmExports["sqlite3_value_int64"]; - Module["_sqlite3_value_subtype"] = wasmExports["sqlite3_value_subtype"]; - Module["_sqlite3_value_pointer"] = wasmExports["sqlite3_value_pointer"]; - Module["_sqlite3_value_type"] = wasmExports["sqlite3_value_type"]; - Module["_sqlite3_value_nochange"] = wasmExports["sqlite3_value_nochange"]; - Module["_sqlite3_value_frombind"] = wasmExports["sqlite3_value_frombind"]; - Module["_sqlite3_value_dup"] = wasmExports["sqlite3_value_dup"]; - Module["_sqlite3_value_free"] = wasmExports["sqlite3_value_free"]; - Module["_sqlite3_result_blob"] = wasmExports["sqlite3_result_blob"]; - Module["_sqlite3_result_error_toobig"] = wasmExports["sqlite3_result_error_toobig"]; - Module["_sqlite3_result_error_nomem"] = wasmExports["sqlite3_result_error_nomem"]; - Module["_sqlite3_result_double"] = wasmExports["sqlite3_result_double"]; - Module["_sqlite3_result_error"] = wasmExports["sqlite3_result_error"]; - Module["_sqlite3_result_int"] = wasmExports["sqlite3_result_int"]; - Module["_sqlite3_result_int64"] = wasmExports["sqlite3_result_int64"]; - Module["_sqlite3_result_null"] = wasmExports["sqlite3_result_null"]; - Module["_sqlite3_result_pointer"] = wasmExports["sqlite3_result_pointer"]; - Module["_sqlite3_result_subtype"] = wasmExports["sqlite3_result_subtype"]; - Module["_sqlite3_result_text"] = wasmExports["sqlite3_result_text"]; - Module["_sqlite3_result_zeroblob"] = wasmExports["sqlite3_result_zeroblob"]; - Module["_sqlite3_result_zeroblob64"] = wasmExports["sqlite3_result_zeroblob64"]; - Module["_sqlite3_result_error_code"] = wasmExports["sqlite3_result_error_code"]; - Module["_sqlite3_user_data"] = wasmExports["sqlite3_user_data"]; - Module["_sqlite3_context_db_handle"] = wasmExports["sqlite3_context_db_handle"]; - Module["_sqlite3_vtab_nochange"] = wasmExports["sqlite3_vtab_nochange"]; - Module["_sqlite3_vtab_in_first"] = wasmExports["sqlite3_vtab_in_first"]; - Module["_sqlite3_vtab_in_next"] = wasmExports["sqlite3_vtab_in_next"]; - Module["_sqlite3_aggregate_context"] = wasmExports["sqlite3_aggregate_context"]; - Module["_sqlite3_get_auxdata"] = wasmExports["sqlite3_get_auxdata"]; - Module["_sqlite3_set_auxdata"] = wasmExports["sqlite3_set_auxdata"]; - Module["_sqlite3_column_count"] = wasmExports["sqlite3_column_count"]; - Module["_sqlite3_data_count"] = wasmExports["sqlite3_data_count"]; - Module["_sqlite3_column_blob"] = wasmExports["sqlite3_column_blob"]; - Module["_sqlite3_column_bytes"] = wasmExports["sqlite3_column_bytes"]; - Module["_sqlite3_column_double"] = wasmExports["sqlite3_column_double"]; - Module["_sqlite3_column_value"] = wasmExports["sqlite3_column_value"]; - Module["_sqlite3_column_decltype"] = wasmExports["sqlite3_column_decltype"]; - Module["_sqlite3_column_database_name"] = wasmExports["sqlite3_column_database_name"]; - Module["_sqlite3_column_table_name"] = wasmExports["sqlite3_column_table_name"]; - Module["_sqlite3_column_origin_name"] = wasmExports["sqlite3_column_origin_name"]; - Module["_sqlite3_bind_blob"] = wasmExports["sqlite3_bind_blob"]; - Module["_sqlite3_bind_double"] = wasmExports["sqlite3_bind_double"]; - Module["_sqlite3_bind_int"] = wasmExports["sqlite3_bind_int"]; - Module["_sqlite3_bind_int64"] = wasmExports["sqlite3_bind_int64"]; - Module["_sqlite3_bind_null"] = wasmExports["sqlite3_bind_null"]; - Module["_sqlite3_bind_pointer"] = wasmExports["sqlite3_bind_pointer"]; - Module["_sqlite3_bind_text"] = wasmExports["sqlite3_bind_text"]; - Module["_sqlite3_bind_parameter_count"] = wasmExports["sqlite3_bind_parameter_count"]; - Module["_sqlite3_bind_parameter_name"] = wasmExports["sqlite3_bind_parameter_name"]; - Module["_sqlite3_bind_parameter_index"] = wasmExports["sqlite3_bind_parameter_index"]; - Module["_sqlite3_db_handle"] = wasmExports["sqlite3_db_handle"]; - Module["_sqlite3_stmt_readonly"] = wasmExports["sqlite3_stmt_readonly"]; - Module["_sqlite3_stmt_isexplain"] = wasmExports["sqlite3_stmt_isexplain"]; - Module["_sqlite3_stmt_explain"] = wasmExports["sqlite3_stmt_explain"]; - Module["_sqlite3_stmt_busy"] = wasmExports["sqlite3_stmt_busy"]; - Module["_sqlite3_next_stmt"] = wasmExports["sqlite3_next_stmt"]; - Module["_sqlite3_stmt_status"] = wasmExports["sqlite3_stmt_status"]; - Module["_sqlite3_sql"] = wasmExports["sqlite3_sql"]; - Module["_sqlite3_expanded_sql"] = wasmExports["sqlite3_expanded_sql"]; - Module["_sqlite3_preupdate_old"] = wasmExports["sqlite3_preupdate_old"]; - Module["_sqlite3_preupdate_count"] = wasmExports["sqlite3_preupdate_count"]; - Module["_sqlite3_preupdate_depth"] = wasmExports["sqlite3_preupdate_depth"]; - Module["_sqlite3_preupdate_blobwrite"] = wasmExports["sqlite3_preupdate_blobwrite"]; - Module["_sqlite3_preupdate_new"] = wasmExports["sqlite3_preupdate_new"]; - Module["_sqlite3_value_numeric_type"] = wasmExports["sqlite3_value_numeric_type"]; - Module["_sqlite3_set_authorizer"] = wasmExports["sqlite3_set_authorizer"]; - Module["_sqlite3_strglob"] = wasmExports["sqlite3_strglob"]; - Module["_sqlite3_strlike"] = wasmExports["sqlite3_strlike"]; - Module["_sqlite3_auto_extension"] = wasmExports["sqlite3_auto_extension"]; - Module["_sqlite3_cancel_auto_extension"] = wasmExports["sqlite3_cancel_auto_extension"]; - Module["_sqlite3_reset_auto_extension"] = wasmExports["sqlite3_reset_auto_extension"]; - Module["_sqlite3_prepare_v3"] = wasmExports["sqlite3_prepare_v3"]; - Module["_sqlite3_create_module"] = wasmExports["sqlite3_create_module"]; - Module["_sqlite3_create_module_v2"] = wasmExports["sqlite3_create_module_v2"]; - Module["_sqlite3_drop_modules"] = wasmExports["sqlite3_drop_modules"]; - Module["_sqlite3_declare_vtab"] = wasmExports["sqlite3_declare_vtab"]; - Module["_sqlite3_vtab_on_conflict"] = wasmExports["sqlite3_vtab_on_conflict"]; - Module["_sqlite3_vtab_collation"] = wasmExports["sqlite3_vtab_collation"]; - Module["_sqlite3_vtab_in"] = wasmExports["sqlite3_vtab_in"]; - Module["_sqlite3_vtab_rhs_value"] = wasmExports["sqlite3_vtab_rhs_value"]; - Module["_sqlite3_vtab_distinct"] = wasmExports["sqlite3_vtab_distinct"]; - Module["_sqlite3_keyword_name"] = wasmExports["sqlite3_keyword_name"]; - Module["_sqlite3_keyword_count"] = wasmExports["sqlite3_keyword_count"]; - Module["_sqlite3_keyword_check"] = wasmExports["sqlite3_keyword_check"]; - Module["_sqlite3_complete"] = wasmExports["sqlite3_complete"]; - Module["_sqlite3_libversion"] = wasmExports["sqlite3_libversion"]; - Module["_sqlite3_libversion_number"] = wasmExports["sqlite3_libversion_number"]; - Module["_sqlite3_shutdown"] = wasmExports["sqlite3_shutdown"]; - Module["_sqlite3_last_insert_rowid"] = wasmExports["sqlite3_last_insert_rowid"]; - Module["_sqlite3_set_last_insert_rowid"] = wasmExports["sqlite3_set_last_insert_rowid"]; - Module["_sqlite3_changes64"] = wasmExports["sqlite3_changes64"]; - Module["_sqlite3_changes"] = wasmExports["sqlite3_changes"]; - Module["_sqlite3_total_changes64"] = wasmExports["sqlite3_total_changes64"]; - Module["_sqlite3_total_changes"] = wasmExports["sqlite3_total_changes"]; - Module["_sqlite3_txn_state"] = wasmExports["sqlite3_txn_state"]; - Module["_sqlite3_close_v2"] = wasmExports["sqlite3_close_v2"]; - Module["_sqlite3_busy_handler"] = wasmExports["sqlite3_busy_handler"]; - Module["_sqlite3_progress_handler"] = wasmExports["sqlite3_progress_handler"]; - Module["_sqlite3_busy_timeout"] = wasmExports["sqlite3_busy_timeout"]; - Module["_sqlite3_interrupt"] = wasmExports["sqlite3_interrupt"]; - Module["_sqlite3_is_interrupted"] = wasmExports["sqlite3_is_interrupted"]; - Module["_sqlite3_create_function"] = wasmExports["sqlite3_create_function"]; - Module["_sqlite3_create_function_v2"] = wasmExports["sqlite3_create_function_v2"]; - Module["_sqlite3_create_window_function"] = wasmExports["sqlite3_create_window_function"]; - Module["_sqlite3_overload_function"] = wasmExports["sqlite3_overload_function"]; - Module["_sqlite3_trace_v2"] = wasmExports["sqlite3_trace_v2"]; - Module["_sqlite3_commit_hook"] = wasmExports["sqlite3_commit_hook"]; - Module["_sqlite3_update_hook"] = wasmExports["sqlite3_update_hook"]; - Module["_sqlite3_rollback_hook"] = wasmExports["sqlite3_rollback_hook"]; - Module["_sqlite3_preupdate_hook"] = wasmExports["sqlite3_preupdate_hook"]; - Module["_sqlite3_set_errmsg"] = wasmExports["sqlite3_set_errmsg"]; - Module["_sqlite3_error_offset"] = wasmExports["sqlite3_error_offset"]; - Module["_sqlite3_errcode"] = wasmExports["sqlite3_errcode"]; - Module["_sqlite3_extended_errcode"] = wasmExports["sqlite3_extended_errcode"]; - Module["_sqlite3_errstr"] = wasmExports["sqlite3_errstr"]; - Module["_sqlite3_limit"] = wasmExports["sqlite3_limit"]; - Module["_sqlite3_open"] = wasmExports["sqlite3_open"]; - Module["_sqlite3_open_v2"] = wasmExports["sqlite3_open_v2"]; - Module["_sqlite3_create_collation"] = wasmExports["sqlite3_create_collation"]; - Module["_sqlite3_create_collation_v2"] = wasmExports["sqlite3_create_collation_v2"]; - Module["_sqlite3_collation_needed"] = wasmExports["sqlite3_collation_needed"]; - Module["_sqlite3_get_autocommit"] = wasmExports["sqlite3_get_autocommit"]; - Module["_sqlite3_table_column_metadata"] = wasmExports["sqlite3_table_column_metadata"]; - Module["_sqlite3_extended_result_codes"] = wasmExports["sqlite3_extended_result_codes"]; - Module["_sqlite3_uri_key"] = wasmExports["sqlite3_uri_key"]; - Module["_sqlite3_uri_int64"] = wasmExports["sqlite3_uri_int64"]; - Module["_sqlite3_db_name"] = wasmExports["sqlite3_db_name"]; - Module["_sqlite3_db_filename"] = wasmExports["sqlite3_db_filename"]; - Module["_sqlite3_db_readonly"] = wasmExports["sqlite3_db_readonly"]; - Module["_sqlite3_compileoption_used"] = wasmExports["sqlite3_compileoption_used"]; - Module["_sqlite3_compileoption_get"] = wasmExports["sqlite3_compileoption_get"]; - Module["_sqlite3session_diff"] = wasmExports["sqlite3session_diff"]; - Module["_sqlite3session_attach"] = wasmExports["sqlite3session_attach"]; - Module["_sqlite3session_create"] = wasmExports["sqlite3session_create"]; - Module["_sqlite3session_delete"] = wasmExports["sqlite3session_delete"]; - Module["_sqlite3session_table_filter"] = wasmExports["sqlite3session_table_filter"]; - Module["_sqlite3session_changeset"] = wasmExports["sqlite3session_changeset"]; - Module["_sqlite3session_changeset_strm"] = wasmExports["sqlite3session_changeset_strm"]; - Module["_sqlite3session_patchset_strm"] = wasmExports["sqlite3session_patchset_strm"]; - Module["_sqlite3session_patchset"] = wasmExports["sqlite3session_patchset"]; - Module["_sqlite3session_enable"] = wasmExports["sqlite3session_enable"]; - Module["_sqlite3session_indirect"] = wasmExports["sqlite3session_indirect"]; - Module["_sqlite3session_isempty"] = wasmExports["sqlite3session_isempty"]; - Module["_sqlite3session_memory_used"] = wasmExports["sqlite3session_memory_used"]; - Module["_sqlite3session_object_config"] = wasmExports["sqlite3session_object_config"]; - Module["_sqlite3session_changeset_size"] = wasmExports["sqlite3session_changeset_size"]; - Module["_sqlite3changeset_start"] = wasmExports["sqlite3changeset_start"]; - Module["_sqlite3changeset_start_v2"] = wasmExports["sqlite3changeset_start_v2"]; - Module["_sqlite3changeset_start_strm"] = wasmExports["sqlite3changeset_start_strm"]; - Module["_sqlite3changeset_start_v2_strm"] = wasmExports["sqlite3changeset_start_v2_strm"]; - Module["_sqlite3changeset_next"] = wasmExports["sqlite3changeset_next"]; - Module["_sqlite3changeset_op"] = wasmExports["sqlite3changeset_op"]; - Module["_sqlite3changeset_pk"] = wasmExports["sqlite3changeset_pk"]; - Module["_sqlite3changeset_old"] = wasmExports["sqlite3changeset_old"]; - Module["_sqlite3changeset_new"] = wasmExports["sqlite3changeset_new"]; - Module["_sqlite3changeset_conflict"] = wasmExports["sqlite3changeset_conflict"]; - Module["_sqlite3changeset_fk_conflicts"] = wasmExports["sqlite3changeset_fk_conflicts"]; - Module["_sqlite3changeset_finalize"] = wasmExports["sqlite3changeset_finalize"]; - Module["_sqlite3changeset_invert"] = wasmExports["sqlite3changeset_invert"]; - Module["_sqlite3changeset_invert_strm"] = wasmExports["sqlite3changeset_invert_strm"]; - Module["_sqlite3changeset_apply_v2"] = wasmExports["sqlite3changeset_apply_v2"]; - Module["_sqlite3changeset_apply_v3"] = wasmExports["sqlite3changeset_apply_v3"]; - Module["_sqlite3changeset_apply"] = wasmExports["sqlite3changeset_apply"]; - Module["_sqlite3changeset_apply_v3_strm"] = wasmExports["sqlite3changeset_apply_v3_strm"]; - Module["_sqlite3changeset_apply_v2_strm"] = wasmExports["sqlite3changeset_apply_v2_strm"]; - Module["_sqlite3changeset_apply_strm"] = wasmExports["sqlite3changeset_apply_strm"]; - Module["_sqlite3changegroup_new"] = wasmExports["sqlite3changegroup_new"]; - Module["_sqlite3changegroup_add"] = wasmExports["sqlite3changegroup_add"]; - Module["_sqlite3changegroup_output"] = wasmExports["sqlite3changegroup_output"]; - Module["_sqlite3changegroup_add_strm"] = wasmExports["sqlite3changegroup_add_strm"]; - Module["_sqlite3changegroup_output_strm"] = wasmExports["sqlite3changegroup_output_strm"]; - Module["_sqlite3changegroup_delete"] = wasmExports["sqlite3changegroup_delete"]; - Module["_sqlite3changeset_concat"] = wasmExports["sqlite3changeset_concat"]; - Module["_sqlite3changeset_concat_strm"] = wasmExports["sqlite3changeset_concat_strm"]; - Module["_sqlite3session_config"] = wasmExports["sqlite3session_config"]; - Module["_sqlite3_sourceid"] = wasmExports["sqlite3_sourceid"]; - Module["_sqlite3__wasm_pstack_ptr"] = wasmExports["sqlite3__wasm_pstack_ptr"]; - Module["_sqlite3__wasm_pstack_restore"] = wasmExports["sqlite3__wasm_pstack_restore"]; - Module["_sqlite3__wasm_pstack_alloc"] = wasmExports["sqlite3__wasm_pstack_alloc"]; - Module["_sqlite3__wasm_pstack_remaining"] = wasmExports["sqlite3__wasm_pstack_remaining"]; - Module["_sqlite3__wasm_pstack_quota"] = wasmExports["sqlite3__wasm_pstack_quota"]; - Module["_sqlite3__wasm_test_struct"] = wasmExports["sqlite3__wasm_test_struct"]; - Module["_sqlite3__wasm_enum_json"] = wasmExports["sqlite3__wasm_enum_json"]; - Module["_sqlite3__wasm_vfs_unlink"] = wasmExports["sqlite3__wasm_vfs_unlink"]; - Module["_sqlite3__wasm_db_vfs"] = wasmExports["sqlite3__wasm_db_vfs"]; - Module["_sqlite3__wasm_db_reset"] = wasmExports["sqlite3__wasm_db_reset"]; - Module["_sqlite3__wasm_db_export_chunked"] = wasmExports["sqlite3__wasm_db_export_chunked"]; - Module["_sqlite3__wasm_db_serialize"] = wasmExports["sqlite3__wasm_db_serialize"]; - Module["_sqlite3__wasm_vfs_create_file"] = wasmExports["sqlite3__wasm_vfs_create_file"]; - Module["_sqlite3__wasm_posix_create_file"] = wasmExports["sqlite3__wasm_posix_create_file"]; - Module["_sqlite3__wasm_kvvfsMakeKey"] = wasmExports["sqlite3__wasm_kvvfsMakeKey"]; - Module["_sqlite3__wasm_kvvfs_methods"] = wasmExports["sqlite3__wasm_kvvfs_methods"]; - Module["_sqlite3__wasm_vtab_config"] = wasmExports["sqlite3__wasm_vtab_config"]; - Module["_sqlite3__wasm_db_config_ip"] = wasmExports["sqlite3__wasm_db_config_ip"]; - Module["_sqlite3__wasm_db_config_pii"] = wasmExports["sqlite3__wasm_db_config_pii"]; - Module["_sqlite3__wasm_db_config_s"] = wasmExports["sqlite3__wasm_db_config_s"]; - Module["_sqlite3__wasm_config_i"] = wasmExports["sqlite3__wasm_config_i"]; - Module["_sqlite3__wasm_config_ii"] = wasmExports["sqlite3__wasm_config_ii"]; - Module["_sqlite3__wasm_config_j"] = wasmExports["sqlite3__wasm_config_j"]; - Module["_sqlite3__wasm_qfmt_token"] = wasmExports["sqlite3__wasm_qfmt_token"]; - Module["_sqlite3__wasm_kvvfs_decode"] = wasmExports["sqlite3__wasm_kvvfs_decode"]; - Module["_sqlite3__wasm_kvvfs_encode"] = wasmExports["sqlite3__wasm_kvvfs_encode"]; - Module["_sqlite3__wasm_init_wasmfs"] = wasmExports["sqlite3__wasm_init_wasmfs"]; - Module["_sqlite3__wasm_test_intptr"] = wasmExports["sqlite3__wasm_test_intptr"]; - Module["_sqlite3__wasm_test_voidptr"] = wasmExports["sqlite3__wasm_test_voidptr"]; - Module["_sqlite3__wasm_test_int64_max"] = wasmExports["sqlite3__wasm_test_int64_max"]; - Module["_sqlite3__wasm_test_int64_min"] = wasmExports["sqlite3__wasm_test_int64_min"]; - Module["_sqlite3__wasm_test_int64_times2"] = wasmExports["sqlite3__wasm_test_int64_times2"]; - Module["_sqlite3__wasm_test_int64_minmax"] = wasmExports["sqlite3__wasm_test_int64_minmax"]; - Module["_sqlite3__wasm_test_int64ptr"] = wasmExports["sqlite3__wasm_test_int64ptr"]; - Module["_sqlite3__wasm_test_stack_overflow"] = wasmExports["sqlite3__wasm_test_stack_overflow"]; - Module["_sqlite3__wasm_test_str_hello"] = wasmExports["sqlite3__wasm_test_str_hello"]; - Module["_sqlite3__wasm_SQLTester_strglob"] = wasmExports["sqlite3__wasm_SQLTester_strglob"]; - Module["_malloc"] = wasmExports["malloc"]; - Module["_free"] = wasmExports["free"]; - Module["_realloc"] = wasmExports["realloc"]; - _emscripten_builtin_memalign = wasmExports["emscripten_builtin_memalign"]; - wasmExports["_emscripten_stack_restore"]; - wasmExports["_emscripten_stack_alloc"]; - wasmExports["emscripten_stack_get_current"]; - wasmExports["__indirect_function_table"]; - } - var wasmImports = { - __syscall_chmod: ___syscall_chmod, - __syscall_faccessat: ___syscall_faccessat, - __syscall_fchmod: ___syscall_fchmod, - __syscall_fchown32: ___syscall_fchown32, - __syscall_fcntl64: ___syscall_fcntl64, - __syscall_fstat64: ___syscall_fstat64, - __syscall_ftruncate64: ___syscall_ftruncate64, - __syscall_getcwd: ___syscall_getcwd, - __syscall_ioctl: ___syscall_ioctl, - __syscall_lstat64: ___syscall_lstat64, - __syscall_mkdirat: ___syscall_mkdirat, - __syscall_newfstatat: ___syscall_newfstatat, - __syscall_openat: ___syscall_openat, - __syscall_readlinkat: ___syscall_readlinkat, - __syscall_rmdir: ___syscall_rmdir, - __syscall_stat64: ___syscall_stat64, - __syscall_unlinkat: ___syscall_unlinkat, - __syscall_utimensat: ___syscall_utimensat, - _localtime_js: __localtime_js, - _mmap_js: __mmap_js, - _munmap_js: __munmap_js, - _tzset_js: __tzset_js, - clock_time_get: _clock_time_get, - emscripten_date_now: _emscripten_date_now, - emscripten_get_heap_max: _emscripten_get_heap_max, - emscripten_get_now: _emscripten_get_now, - emscripten_resize_heap: _emscripten_resize_heap, - environ_get: _environ_get, - environ_sizes_get: _environ_sizes_get, - fd_close: _fd_close, - fd_fdstat_get: _fd_fdstat_get, - fd_read: _fd_read, - fd_seek: _fd_seek, - fd_sync: _fd_sync, - fd_write: _fd_write, - memory: wasmMemory - }; - function run() { - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } - preRun(); - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } - function doRun() { - Module["calledRun"] = true; - if (ABORT) return; - initRuntime(); - readyPromiseResolve?.(Module); - Module["onRuntimeInitialized"]?.(); - postRun(); - } - if (Module["setStatus"]) { - Module["setStatus"]("Running..."); - setTimeout(() => { - setTimeout(() => Module["setStatus"](""), 1); - doRun(); - }, 1); - } else doRun(); - } - var wasmExports = await createWasm(); - run(); - /** - post-js-header.js is to be prepended to other code to create - post-js.js for use with Emscripten's --post-js flag, so it gets - injected in the earliest stages of sqlite3InitModule(). - - Running this function will bootstrap the library and return - a Promise to the sqlite3 namespace object. - - In the canonical builds, this gets called by extern-post-js.c-pp.js - */ - Module.runSQLite3PostLoadInit = async function(sqlite3InitScriptInfo, EmscriptenModule, sqlite3IsUnderTest) { - /** ^^^ Don't use Module.postRun, as that runs a different time - depending on whether this file is built with emcc 3.1.x or - 4.0.x. This function name is intentionally obnoxiously verbose to - ensure that we don't collide with current and future Emscripten - symbol names. */ - "use strict"; - delete EmscriptenModule.runSQLite3PostLoadInit; - globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(apiConfig = globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) { - if (sqlite3ApiBootstrap.sqlite3) { - (sqlite3ApiBootstrap.sqlite3.config || console).warn("sqlite3ApiBootstrap() called multiple times.", "Config and external initializers are ignored on calls after the first."); - return sqlite3ApiBootstrap.sqlite3; - } - const config = Object.assign(Object.create(null), { - exports: void 0, - memory: void 0, - bigIntEnabled: !!globalThis.BigInt64Array, - debug: console.debug.bind(console), - warn: console.warn.bind(console), - error: console.error.bind(console), - log: console.log.bind(console), - wasmfsOpfsDir: "/opfs", - useStdAlloc: false - }, apiConfig || {}); - Object.assign(config, { - allocExportName: config.useStdAlloc ? "malloc" : "sqlite3_malloc", - deallocExportName: config.useStdAlloc ? "free" : "sqlite3_free", - reallocExportName: config.useStdAlloc ? "realloc" : "sqlite3_realloc" - }); - [ - "exports", - "memory", - "functionTable", - "wasmfsOpfsDir" - ].forEach((k) => { - if ("function" === typeof config[k]) config[k] = config[k](); - }); - /** - The main sqlite3 binding API gets installed into this object, - mimicking the C API as closely as we can. The numerous members - names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as - possible, identically to the C-native counterparts, as documented at: - - https://sqlite.org/c3ref/intro.html - - A very few exceptions require an additional level of proxy - function or may otherwise require special attention in the WASM - environment, and all such cases are documented somewhere below - in this file or in sqlite3-api-glue.js. capi members which are - not documented are installed as 1-to-1 proxies for their - C-side counterparts. - */ - const capi = Object.create(null); - /** - Holds state which are specific to the WASM-related - infrastructure and glue code. - - Note that a number of members of this object are injected - dynamically after the api object is fully constructed, so - not all are documented in this file. - */ - const wasm = Object.create(null); - /** Internal helper for SQLite3Error ctor. */ - const __rcStr = (rc) => { - return capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc) || "Unknown result code #" + rc; - }; - /** Internal helper for SQLite3Error ctor. */ - const isInt32 = (n) => "number" === typeof n && n === (n | 0) && n <= 2147483647 && n >= -2147483648; - /** - An Error subclass specifically for reporting DB-level errors and - enabling clients to unambiguously identify such exceptions. - The C-level APIs never throw, but some of the higher-level - C-style APIs do and the object-oriented APIs use exceptions - exclusively to report errors. - */ - class SQLite3Error extends Error { - /** - Constructs this object with a message depending on its arguments: - - If its first argument is an integer, it is assumed to be - an SQLITE_... result code and it is passed to - sqlite3.capi.sqlite3_js_rc_str() to stringify it. - - If called with exactly 2 arguments and the 2nd is an object, - that object is treated as the 2nd argument to the parent - constructor. - - The exception's message is created by concatenating its - arguments with a space between each, except for the - two-args-with-an-object form and that the first argument will - get coerced to a string, as described above, if it's an - integer. - - If passed an integer first argument, the error object's - `resultCode` member will be set to the given integer value, - else it will be set to capi.SQLITE_ERROR. - */ - constructor(...args) { - let rc; - if (args.length) if (isInt32(args[0])) { - rc = args[0]; - if (1 === args.length) super(__rcStr(args[0])); - else { - const rcStr = __rcStr(rc); - if ("object" === typeof args[1]) super(rcStr, args[1]); - else { - args[0] = rcStr + ":"; - super(args.join(" ")); - } - } - } else if (2 === args.length && "object" === typeof args[1]) super(...args); - else super(args.join(" ")); - this.resultCode = rc || capi.SQLITE_ERROR; - this.name = "SQLite3Error"; - } - } - /** - Functionally equivalent to the SQLite3Error constructor but may - be used as part of an expression, e.g.: - - ``` - return someFunction(x) || SQLite3Error.toss(...); - ``` - */ - SQLite3Error.toss = (...args) => { - throw new SQLite3Error(...args); - }; - const toss3 = SQLite3Error.toss; - if (config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)) toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'."); - /** - Returns true if the given BigInt value is small enough to fit - into an int64 value, else false. - */ - const bigIntFits64 = function f(b) { - if (!f._max) { - f._max = BigInt("0x7fffffffffffffff"); - f._min = ~f._max; - } - return b >= f._min && b <= f._max; - }; - /** - Returns true if the given BigInt value is small enough to fit - into an int32, else false. - */ - const bigIntFits32 = (b) => b >= -2147483647n - 1n && b <= 2147483647n; - /** - Returns true if the given BigInt value is small enough to fit - into a double value without loss of precision, else false. - */ - const bigIntFitsDouble = function f(b) { - if (!f._min) { - f._min = Number.MIN_SAFE_INTEGER; - f._max = Number.MAX_SAFE_INTEGER; - } - return b >= f._min && b <= f._max; - }; - /** Returns v if v appears to be a TypedArray, else false. */ - const isTypedArray = (v) => { - return v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT) ? v : false; - }; - /** - Returns true if v appears to be one of our bind()-able TypedArray - types: Uint8Array or Int8Array or ArrayBuffer. Support for - TypedArrays with element sizes >1 is a potential TODO just - waiting on a use case to justify them. Until then, their `buffer` - property can be used to pass them as an ArrayBuffer. If it's not - a bindable array type, a falsy value is returned. - */ - const isBindableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); - /** - Returns true if v appears to be one of the TypedArray types - which is legal for holding SQL code (as opposed to binary blobs). - - Currently this is the same as isBindableTypedArray() but it - seems likely that we'll eventually want to add Uint32Array - and friends to the isBindableTypedArray() list but not to the - isSQLableTypedArray() list. - */ - const isSQLableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); - /** Returns true if isBindableTypedArray(v) does, else throws with a message - that v is not a supported TypedArray value. */ - const affirmBindableTypedArray = (v) => isBindableTypedArray(v) || toss3("Value is not of a supported TypedArray type."); - /** - If v is-a Array, its join("") result is returned. If - isSQLableTypedArray(v) is true then wasm.typedArrayToString(v) is - returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is - returned. Else v is returned as-is. - - Reminder to self: the "return as-is" instead of returning ''+v is - arguably a design mistake but changing it is risky at this point. - */ - const flexibleString = function(v) { - if (isSQLableTypedArray(v)) return wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v, 0, v.length); - else if (Array.isArray(v)) return v.join(""); - else if (wasm.isPtr(v)) v = wasm.cstrToJs(v); - return v; - }; - /** - An Error subclass specifically for reporting Wasm-level malloc() - failure and enabling clients to unambiguously identify such - exceptions. - */ - class WasmAllocError extends Error { - /** - If called with 2 arguments and the 2nd one is an object, it - behaves like the Error constructor, else it concatenates all - arguments together with a single space between each to - construct an error message string. As a special case, if - called with no arguments then it uses a default error - message. - */ - constructor(...args) { - if (2 === args.length && "object" === typeof args[1]) super(...args); - else if (args.length) super(args.join(" ")); - else super("Allocation failed."); - this.resultCode = capi.SQLITE_NOMEM; - this.name = "WasmAllocError"; - } - } - /** - Functionally equivalent to the WasmAllocError constructor but may - be used as part of an expression, e.g.: - - ``` - return someAllocatingFunction(x) || WasmAllocError.toss(...); - ``` - */ - WasmAllocError.toss = (...args) => { - throw new WasmAllocError(...args); - }; - Object.assign(capi, { - sqlite3_bind_blob: void 0, - sqlite3_bind_text: void 0, - sqlite3_create_function_v2: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) => {}, - sqlite3_create_function: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) => {}, - sqlite3_create_window_function: (pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) => {}, - sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags, stmtPtrPtr, strPtrPtr) => {}, - sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen, stmtPtrPtr, strPtrPtr) => {}, - sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg) => {}, - sqlite3_randomness: (n, outPtr) => {} - }); - /** - Various internal-use utilities are added here as needed. They - are bound to an object only so that we have access to them in - the differently-scoped steps of the API bootstrapping - process. At the end of the API setup process, this object gets - removed. These are NOT part of the public API. - */ - const util = { - affirmBindableTypedArray, - flexibleString, - bigIntFits32, - bigIntFits64, - bigIntFitsDouble, - isBindableTypedArray, - isInt32, - isSQLableTypedArray, - isTypedArray, - isUIThread: () => globalThis.window === globalThis && !!globalThis.document, - toss: function(...args) { - throw new Error(args.join(" ")); - }, - toss3, - typedArrayPart: wasm.typedArrayPart, - assert: function(arg, msg) { - if (!arg) util.toss("Assertion failed:", msg); - }, - affirmDbHeader: function(bytes) { - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - const header = "SQLite format 3"; - if (15 > bytes.byteLength) toss3("Input does not contain an SQLite3 database header."); - for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss3("Input does not contain an SQLite3 database header."); - }, - affirmIsDb: function(bytes) { - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - const n = bytes.byteLength; - if (n < 512 || n % 512 !== 0) toss3("Byte array size", n, "is invalid for an SQLite3 db."); - util.affirmDbHeader(bytes); - } - }; - /** - wasm.X properties which are used for configuring the wasm - environment via whwashutil.js. This object gets fleshed out with - a number of WASM-specific utilities, in sqlite3-api-glue.c-pp.js. - */ - Object.assign(wasm, { - exports: config.exports || toss3("Missing API config.exports (WASM module exports)."), - memory: config.memory || config.exports["memory"] || toss3("API config object requires a WebAssembly.Memory object", "in either config.exports.memory (exported)", "or config.memory (imported)."), - pointerSize: "number" === typeof config.exports.sqlite3_libversion() ? 4 : 8, - bigIntEnabled: !!config.bigIntEnabled, - functionTable: config.functionTable, - alloc: void 0, - realloc: void 0, - dealloc: void 0 - }); - /** - wasm.alloc()'s srcTypedArray.byteLength bytes, - populates them with the values from the source - TypedArray, and returns the pointer to that memory. The - returned pointer must eventually be passed to - wasm.dealloc() to clean it up. - - The argument may be a Uint8Array, Int8Array, or ArrayBuffer, - and it throws if passed any other type. - - As a special case, to avoid further special cases where - this is used, if srcTypedArray.byteLength is 0, it - allocates a single byte and sets it to the value - 0. Even in such cases, calls must behave as if the - allocated memory has exactly srcTypedArray.byteLength - bytes. - */ - wasm.allocFromTypedArray = function(srcTypedArray) { - if (srcTypedArray instanceof ArrayBuffer) srcTypedArray = new Uint8Array(srcTypedArray); - affirmBindableTypedArray(srcTypedArray); - const pRet = wasm.alloc(srcTypedArray.byteLength || 1); - wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], Number(pRet)); - return pRet; - }; - { - const keyAlloc = config.allocExportName, keyDealloc = config.deallocExportName, keyRealloc = config.reallocExportName; - for (const key of [ - keyAlloc, - keyDealloc, - keyRealloc - ]) if (!(wasm.exports[key] instanceof Function)) toss3("Missing required exports[", key, "] function."); - wasm.alloc = function f(n) { - return f.impl(n) || WasmAllocError.toss("Failed to allocate", n, " bytes."); - }; - wasm.alloc.impl = wasm.exports[keyAlloc]; - wasm.realloc = function f(m, n) { - const m2 = f.impl(wasm.ptr.coerce(m), n); - return n ? m2 || WasmAllocError.toss("Failed to reallocate", n, " bytes.") : wasm.ptr.null; - }; - wasm.realloc.impl = wasm.exports[keyRealloc]; - wasm.dealloc = function f(m) { - f.impl(wasm.ptr.coerce(m)); - }; - wasm.dealloc.impl = wasm.exports[keyDealloc]; - } - /** - Reports info about compile-time options using - sqlite3_compileoption_get() and sqlite3_compileoption_used(). It - has several distinct uses: - - If optName is an array then it is expected to be a list of - compilation options and this function returns an object - which maps each such option to true or false, indicating - whether or not the given option was included in this - build. That object is returned. - - If optName is an object, its keys are expected to be compilation - options and this function sets each entry to true or false, - indicating whether the compilation option was used or not. That - object is returned. - - If passed no arguments then it returns an object mapping - all known compilation options to their compile-time values, - or boolean true if they are defined with no value. This - result, which is relatively expensive to compute, is cached - and returned for future no-argument calls. - - In all other cases it returns true if the given option was - active when when compiling the sqlite3 module, else false. - - Compile-time option names may optionally include their - "SQLITE_" prefix. When it returns an object of all options, - the prefix is elided. - */ - wasm.compileOptionUsed = function f(optName) { - if (!arguments.length) { - if (f._result) return f._result; - else if (!f._opt) { - f._rx = /^([^=]+)=(.+)/; - f._rxInt = /^-?\d+$/; - f._opt = function(opt, rv) { - const m = f._rx.exec(opt); - rv[0] = m ? m[1] : opt; - rv[1] = m ? f._rxInt.test(m[2]) ? +m[2] : m[2] : true; - }; - } - const rc = Object.create(null), ov = [0, 0]; - let i = 0, k; - while (k = capi.sqlite3_compileoption_get(i++)) { - f._opt(k, ov); - rc[ov[0]] = ov[1]; - } - return f._result = rc; - } else if (Array.isArray(optName)) { - const rc = Object.create(null); - optName.forEach((v) => { - rc[v] = capi.sqlite3_compileoption_used(v); - }); - return rc; - } else if ("object" === typeof optName) { - Object.keys(optName).forEach((k) => { - optName[k] = capi.sqlite3_compileoption_used(k); - }); - return optName; - } - return "string" === typeof optName ? !!capi.sqlite3_compileoption_used(optName) : false; - }; - /** - sqlite3.wasm.pstack (pseudo-stack) holds a special-case allocator - intended solely for short-lived, small data. In practice, it's - primarily used to allocate output pointers. It must not be used - for any memory which needs to outlive the scope in which it's - obtained from pstack. - - The library guarantees only that a minimum of 2kb are available - in this allocator, and it may provide more (it's a build-time - value). pstack.quota and pstack.remaining can be used to get the - total resp. remaining amount of memory. - - It has only a single intended usage pattern: - - ``` - const stackPos = pstack.pointer; - try{ - const ptr = pstack.alloc(8); - // ==> pstack.pointer === ptr - const otherPtr = pstack.alloc(8); - // ==> pstack.pointer === otherPtr - ... - }finally{ - pstack.restore(stackPos); - // ==> pstack.pointer === stackPos - } - ``` - - This allocator is much faster than a general-purpose one but is - limited to usage patterns like the one shown above (which are - pretty common when using sqlite3.capi). - - The memory lives in the WASM heap and can be used with routines - such as wasm.poke() and wasm.heap8u().slice(). - */ - wasm.pstack = Object.assign(Object.create(null), { - restore: wasm.exports.sqlite3__wasm_pstack_restore, - alloc: function(n) { - if ("string" === typeof n && !(n = wasm.sizeofIR(n))) WasmAllocError.toss("Invalid value for pstack.alloc(", arguments[0], ")"); - return wasm.exports.sqlite3__wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate", n, "bytes from the pstack."); - }, - allocChunks: function(n, sz) { - if ("string" === typeof sz && !(sz = wasm.sizeofIR(sz))) WasmAllocError.toss("Invalid size value for allocChunks(", arguments[1], ")"); - const mem = wasm.pstack.alloc(n * sz); - const rc = [mem]; - let i = 1, offset = sz; - for (; i < n; ++i, offset += sz) rc.push(wasm.ptr.add(mem, offset)); - return rc; - }, - allocPtr: (n = 1, safePtrSize = true) => { - return 1 === n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptr.size) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptr.size); - }, - call: function(f) { - const stackPos = wasm.pstack.pointer; - try { - return f(sqlite3); - } finally { - wasm.pstack.restore(stackPos); - } - } - }); - Object.defineProperties(wasm.pstack, { - pointer: { - configurable: false, - iterable: true, - writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_ptr - }, - quota: { - configurable: false, - iterable: true, - writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_quota - }, - remaining: { - configurable: false, - iterable: true, - writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_remaining - } - }); - /** - Docs: https://sqlite.org/wasm/doc/trunk/api-c-style.md#sqlite3_randomness - */ - capi.sqlite3_randomness = (...args) => { - if (1 === args.length && util.isTypedArray(args[0]) && 1 === args[0].BYTES_PER_ELEMENT) { - const ta = args[0]; - if (0 === ta.byteLength) { - wasm.exports.sqlite3_randomness(0, wasm.ptr.null); - return ta; - } - const stack = wasm.pstack.pointer; - try { - let n = ta.byteLength, offset = 0; - const r = wasm.exports.sqlite3_randomness; - const heap = wasm.heap8u(); - const nAlloc = n < 512 ? n : 512; - const ptr = wasm.pstack.alloc(nAlloc); - do { - const j = n > nAlloc ? nAlloc : n; - r(j, ptr); - ta.set(wasm.typedArrayPart(heap, ptr, wasm.ptr.add(ptr, j)), offset); - n -= j; - offset += j; - } while (n > 0); - } catch (e) { - config.error("Highly unexpected (and ignored!) exception in sqlite3_randomness():", e); - } finally { - wasm.pstack.restore(stack); - } - return ta; - } - wasm.exports.sqlite3_randomness(...args); - }; - /** - If the wasm environment has a WASMFS/OPFS-backed persistent - storage directory, its path is returned by this function. If it - does not then it returns "" (noting that "" is a falsy value). - - The first time this is called, this function inspects the current - environment to determine whether WASMFS persistence support is - available and, if it is, enables it (if needed). After the first - call it always returns the cached result. - - If the returned string is not empty, any files stored under the - returned path (recursively) are housed in OPFS storage. If the - returned string is empty, this particular persistent storage - option is not available on the client. - - Though the mount point name returned by this function is intended - to remain stable, clients should not hard-coded it anywhere. - Always call this function to get the path. - - This function is a no-op in most builds of this library, as the - WASMFS capability requires a custom build. - */ - capi.sqlite3_wasmfs_opfs_dir = function() { - if (void 0 !== this.dir) return this.dir; - const pdir = config.wasmfsOpfsDir; - if (!pdir || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !wasm.exports.sqlite3__wasm_init_wasmfs) return this.dir = ""; - try { - if (pdir && 0 === wasm.xCallWrapped("sqlite3__wasm_init_wasmfs", "i32", ["string"], pdir)) return this.dir = pdir; - else return this.dir = ""; - } catch (e) { - return this.dir = ""; - } - }.bind(Object.create(null)); - /** - Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a - non-empty string and the given name starts with (that string + - '/'), else returns false. - */ - capi.sqlite3_wasmfs_filename_is_persistent = function(name) { - const p = capi.sqlite3_wasmfs_opfs_dir(); - return p && name ? name.startsWith(p + "/") : false; - }; - /** - Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name - (defaulting to "main"), returns a truthy value (see below) if - that db uses that VFS, else returns false. If pDb is falsy then - the 3rd argument is ignored and this function returns a truthy - value if the default VFS name matches that of the 2nd argument. - Results are undefined if pDb is truthy but refers to an invalid - pointer. The 3rd argument specifies the database name of the - given database connection to check, defaulting to the main db. - - The 2nd and 3rd arguments may either be a JS string or a WASM - C-string. If the 2nd argument is a NULL WASM pointer, the default - VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is - assumed. - - The truthy value it returns is a pointer to the `sqlite3_vfs` - object. - - To permit safe use of this function from APIs which may be called - via C (like SQL UDFs), this function does not throw: if bad - arguments cause a conversion error when passing into wasm-space, - false is returned. - */ - capi.sqlite3_js_db_uses_vfs = function(pDb, vfsName, dbName = 0) { - try { - const pK = capi.sqlite3_vfs_find(vfsName); - if (!pK) return false; - else if (!pDb) return pK === capi.sqlite3_vfs_find(0) ? pK : false; - else return pK === capi.sqlite3_js_db_vfs(pDb, dbName) ? pK : false; - } catch (e) { - return false; - } - }; - /** - Returns an array of the names of all currently-registered sqlite3 - VFSes. - */ - capi.sqlite3_js_vfs_list = function() { - const rc = []; - let pVfs = capi.sqlite3_vfs_find(wasm.ptr.null); - while (pVfs) { - const oVfs = new capi.sqlite3_vfs(pVfs); - rc.push(wasm.cstrToJs(oVfs.$zName)); - pVfs = oVfs.$pNext; - oVfs.dispose(); - } - return rc; - }; - /** - A convenience wrapper around sqlite3_serialize() which serializes - the given `sqlite3*` pointer to a Uint8Array. The first argument - may be either an `sqlite3*` or an sqlite3.oo1.DB instance. - - On success it returns a Uint8Array. If the schema is empty, an - empty array is returned. - - `schema` is the schema to serialize. It may be a WASM C-string - pointer or a JS string. If it is falsy, it defaults to `"main"`. - - On error it throws with a description of the problem. - */ - capi.sqlite3_js_db_export = function(pDb, schema = 0) { - pDb = wasm.xWrap.testConvertArg("sqlite3*", pDb); - if (!pDb) toss3("Invalid sqlite3* argument."); - if (!wasm.bigIntEnabled) toss3("BigInt support is not enabled."); - const scope = wasm.scopedAllocPush(); - let pOut; - try { - const pSize = wasm.scopedAlloc(8 + wasm.ptr.size); - const ppOut = wasm.ptr.add(pSize, 8); - /** - Maintenance reminder, since this cost a full hour of grief - and confusion: if the order of pSize/ppOut are reversed in - that memory block, fetching the value of pSize after the - export reads a garbage size because it's not on an 8-byte - memory boundary! - */ - const zSchema = schema ? wasm.isPtr(schema) ? schema : wasm.scopedAllocCString("" + schema) : wasm.ptr.null; - let rc = wasm.exports.sqlite3__wasm_db_serialize(pDb, zSchema, ppOut, pSize, 0); - if (rc) toss3("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc)); - pOut = wasm.peekPtr(ppOut); - const nOut = wasm.peek(pSize, "i64"); - rc = nOut ? wasm.heap8u().slice(Number(pOut), Number(pOut) + Number(nOut)) : new Uint8Array(); - return rc; - } finally { - if (pOut) wasm.exports.sqlite3_free(pOut); - wasm.scopedAllocPop(scope); - } - }; - /** - Given a `sqlite3*` and a database name (JS string or WASM - C-string pointer, which may be 0), returns a pointer to the - sqlite3_vfs responsible for it. If the given db name is null/0, - or not provided, then "main" is assumed. - */ - capi.sqlite3_js_db_vfs = (dbPointer, dbName = wasm.ptr.null) => util.sqlite3__wasm_db_vfs(dbPointer, dbName); - /** - A thin wrapper around capi.sqlite3_aggregate_context() which - behaves the same except that it throws a WasmAllocError if that - function returns 0. As a special case, if n is falsy it does - _not_ throw if that function returns 0. That special case is - intended for use with xFinal() implementations. - */ - capi.sqlite3_js_aggregate_context = (pCtx, n) => { - return capi.sqlite3_aggregate_context(pCtx, n) || (n ? WasmAllocError.toss("Cannot allocate", n, "bytes for sqlite3_aggregate_context()") : 0); - }; - /** - If the current environment supports the POSIX file APIs, this routine - creates (or overwrites) the given file using those APIs. This is - primarily intended for use in Emscripten-based builds where the POSIX - APIs are transparently proxied by an in-memory virtual filesystem. - It may behave differently in other environments. - - The first argument must be either a JS string or WASM C-string - holding the filename. This routine does _not_ create intermediary - directories if the filename has a directory part. - - The 2nd argument may either a valid WASM memory pointer, an - ArrayBuffer, or a Uint8Array. The 3rd must be the length, in - bytes, of the data array to copy. If the 2nd argument is an - ArrayBuffer or Uint8Array and the 3rd is not a positive integer - then the 3rd defaults to the array's byteLength value. - - Results are undefined if data is a WASM pointer and dataLen is - exceeds data's bounds. - - Throws if any arguments are invalid or if creating or writing to - the file fails. - - Added in 3.43 as an alternative for the deprecated - sqlite3_js_vfs_create_file(). - */ - capi.sqlite3_js_posix_create_file = function(filename, data, dataLen) { - let pData; - if (data && wasm.isPtr(data)) pData = data; - else if (data instanceof ArrayBuffer || data instanceof Uint8Array) { - pData = wasm.allocFromTypedArray(data); - if (arguments.length < 3 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; - } else SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file()."); - try { - if (!util.isInt32(dataLen) || dataLen < 0) SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file()."); - const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen); - if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); - } finally { - if (pData && pData !== data) wasm.dealloc(pData); - } - }; - /** - Deprecation warning: this function does not work properly in - debug builds of sqlite3 because its out-of-scope use of the - sqlite3_vfs API triggers assertions in the core library. That - was unfortunately not discovered until 2023-08-11. This function - is now deprecated. It should not be used in new code and should - be removed from existing code. - - Alternative options: - - - The "unix" VFS and its variants can get equivalent - functionality with sqlite3_js_posix_create_file(). - - - OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs" - VFS, or the importDb() method of the PoolUtil object provided - by the "opfs-sahpool" OPFS (noting that its VFS name may differ - depending on client-side configuration). We cannot proxy those - from here because the former is necessarily asynchronous and - the latter requires information not available to this function. - - Historical (deprecated) behaviour: - - Creates a file using the storage appropriate for the given - sqlite3_vfs. The first argument may be a VFS name (JS string - only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or - a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the - default VFS. If passed a string which does not resolve using - sqlite3_vfs_find(), an exception is thrown. (Note that a WASM - C-string is not accepted because it is impossible to - distinguish from a C-level `sqlite3_vfs*`.) - - The second argument, the filename, must be a JS or WASM C-string. - - The 3rd may either be falsy, a valid WASM memory pointer, an - ArrayBuffer, or a Uint8Array. The 4th must be the length, in - bytes, of the data array to copy. If the 3rd argument is an - ArrayBuffer or Uint8Array and the 4th is not a positive integer - then the 4th defaults to the array's byteLength value. - - If data is falsy then a file is created with dataLen bytes filled - with uninitialized data (whatever truncate() leaves there). If - data is not falsy then a file is created or truncated and it is - filled with the first dataLen bytes of the data source. - - Throws if any arguments are invalid or if creating or writing to - the file fails. - - Note that most VFSes do _not_ automatically create directory - parts of filenames, nor do all VFSes have a concept of - directories. If the given filename is not valid for the given - VFS, an exception will be thrown. This function exists primarily - to assist in implementing file-upload capability, with the caveat - that clients must have some idea of the VFS into which they want - to upload and that VFS must support the operation. - - VFS-specific notes: - - - "memdb": results are undefined. - - - "kvvfs": will fail with an I/O error due to strict internal - requirements of that VFS's xTruncate(). - - - "unix" and related: will use the WASM build's equivalent of the - POSIX I/O APIs. This will work so long as neither a specific - VFS nor the WASM environment imposes requirements which break - it. (Much later: it turns out that debug builds of the library - impose such requirements, in that they assert() that dataLen is - an even multiple of a valid db page size.) - - - "opfs": uses OPFS storage and creates directory parts of the - filename. It can only be used to import an SQLite3 database - file and will fail if given anything else. - */ - capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen) { - config.warn("sqlite3_js_vfs_create_file() is deprecated and", "should be avoided because it can lead to C-level crashes.", "See its documentation for alternatives."); - let pData; - if (data) if (wasm.isPtr(data)) pData = data; - else { - if (data instanceof ArrayBuffer) data = new Uint8Array(data); - if (data instanceof Uint8Array) { - pData = wasm.allocFromTypedArray(data); - if (arguments.length < 4 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; - } else SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file()."); - } - else pData = 0; - if (!util.isInt32(dataLen) || dataLen < 0) { - if (pData && pData !== data) wasm.dealloc(pData); - SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file()."); - } - try { - const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen); - if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); - } finally { - if (pData && pData !== data) wasm.dealloc(pData); - } - }; - /** - Converts SQL input from a variety of convenient formats - to plain strings. - - If v is a string, it is returned as-is. If it is-a Array, its - join("") result is returned. If is is a Uint8Array, Int8Array, - or ArrayBuffer, it is assumed to hold UTF-8-encoded text and is - decoded to a string. If it looks like a WASM pointer, - wasm.cstrToJs(sql) is returned. Else undefined is returned. - - Added in 3.44 - */ - capi.sqlite3_js_sql_to_string = (sql) => { - if ("string" === typeof sql) return sql; - const x = flexibleString(v); - return x === v ? void 0 : x; - }; - /** - Wraps all known variants of the C-side variadic - sqlite3_db_config(). - - Full docs: https://sqlite.org/c3ref/db_config.html - - Returns capi.SQLITE_MISUSE if op is not a valid operation ID. - - The variants which take `(int, int*)` arguments treat a - missing or falsy pointer argument as 0. - */ - capi.sqlite3_db_config = function(pDb, op, ...args) { - switch (op) { - case capi.SQLITE_DBCONFIG_ENABLE_FKEY: - case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: - case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: - case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: - case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: - case capi.SQLITE_DBCONFIG_ENABLE_QPSG: - case capi.SQLITE_DBCONFIG_TRIGGER_EQP: - case capi.SQLITE_DBCONFIG_RESET_DATABASE: - case capi.SQLITE_DBCONFIG_DEFENSIVE: - case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA: - case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: - case capi.SQLITE_DBCONFIG_DQS_DML: - case capi.SQLITE_DBCONFIG_DQS_DDL: - case capi.SQLITE_DBCONFIG_ENABLE_VIEW: - case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: - case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA: - case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS: - case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER: - case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: - case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: - case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: - if (!this.ip) this.ip = wasm.xWrap("sqlite3__wasm_db_config_ip", "int", [ - "sqlite3*", - "int", - "int", - "*" - ]); - return this.ip(pDb, op, args[0], args[1] || 0); - case capi.SQLITE_DBCONFIG_LOOKASIDE: - if (!this.pii) this.pii = wasm.xWrap("sqlite3__wasm_db_config_pii", "int", [ - "sqlite3*", - "int", - "*", - "int", - "int" - ]); - return this.pii(pDb, op, args[0], args[1], args[2]); - case capi.SQLITE_DBCONFIG_MAINDBNAME: - if (!this.s) this.s = wasm.xWrap("sqlite3__wasm_db_config_s", "int", [ - "sqlite3*", - "int", - "string:static" - ]); - return this.s(pDb, op, args[0]); - default: return capi.SQLITE_MISUSE; - } - }.bind(Object.create(null)); - /** - Given a (sqlite3_value*), this function attempts to convert it - to an equivalent JS value with as much fidelity as feasible and - return it. - - By default it throws if it cannot determine any sensible - conversion. If passed a falsy second argument, it instead returns - `undefined` if no suitable conversion is found. Note that there - is no conversion from SQL to JS which results in the `undefined` - value, so `undefined` has an unambiguous meaning here. It will - always throw a WasmAllocError if allocating memory for a - conversion fails. - - Caveats: - - - It does not support sqlite3_value_to_pointer() conversions - because those require a type name string which this function - does not have and cannot sensibly be given at the level of the - API where this is used (e.g. automatically converting UDF - arguments). Clients using sqlite3_value_to_pointer(), and its - related APIs, will need to manage those themselves. - */ - capi.sqlite3_value_to_js = function(pVal, throwIfCannotConvert = true) { - let arg; - const valType = capi.sqlite3_value_type(pVal); - switch (valType) { - case capi.SQLITE_INTEGER: - if (wasm.bigIntEnabled) { - arg = capi.sqlite3_value_int64(pVal); - if (util.bigIntFitsDouble(arg)) arg = Number(arg); - } else arg = capi.sqlite3_value_double(pVal); - break; - case capi.SQLITE_FLOAT: - arg = capi.sqlite3_value_double(pVal); - break; - case capi.SQLITE_TEXT: - arg = capi.sqlite3_value_text(pVal); - break; - case capi.SQLITE_BLOB: { - const n = capi.sqlite3_value_bytes(pVal); - const pBlob = capi.sqlite3_value_blob(pVal); - if (n && !pBlob) sqlite3.WasmAllocError.toss("Cannot allocate memory for blob argument of", n, "byte(s)"); - arg = n ? wasm.heap8u().slice(Number(pBlob), Number(pBlob) + Number(n)) : null; - break; - } - case capi.SQLITE_NULL: - arg = null; - break; - default: - if (throwIfCannotConvert) toss3(capi.SQLITE_MISMATCH, "Unhandled sqlite3_value_type():", valType); - arg = void 0; - } - return arg; - }; - /** - Requires a C-style array of `sqlite3_value*` objects and the - number of entries in that array. Returns a JS array containing - the results of passing each C array entry to - sqlite3_value_to_js(). The 3rd argument to this function is - passed on as the 2nd argument to that one. - */ - capi.sqlite3_values_to_js = function(argc, pArgv, throwIfCannotConvert = true) { - let i; - const tgt = []; - for (i = 0; i < argc; ++i) - /** - Curiously: despite ostensibly requiring 8-byte - alignment, the pArgv array is parcelled into chunks of - 4 bytes (1 pointer each). The values those point to - have 8-byte alignment but the individual argv entries - do not. - */ - tgt.push(capi.sqlite3_value_to_js(wasm.peekPtr(wasm.ptr.add(pArgv, wasm.ptr.size * i)), throwIfCannotConvert)); - return tgt; - }; - /** - Calls either sqlite3_result_error_nomem(), if e is-a - WasmAllocError, or sqlite3_result_error(). In the latter case, - the second argument is coerced to a string to create the error - message. - - The first argument is a (sqlite3_context*). Returns void. - Does not throw. - */ - capi.sqlite3_result_error_js = function(pCtx, e) { - if (e instanceof WasmAllocError) capi.sqlite3_result_error_nomem(pCtx); - else capi.sqlite3_result_error(pCtx, "" + e, -1); - }; - /** - This function passes its 2nd argument to one of the - sqlite3_result_xyz() routines, depending on the type of that - argument: - - - If (val instanceof Error), this function passes it to - sqlite3_result_error_js(). - - `null`: `sqlite3_result_null()` - - `boolean`: `sqlite3_result_int()` with a value of 0 or 1. - - `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or - `sqlite3_result_double()`, depending on the range of the number - and whether or not int64 support is enabled. - - `bigint`: similar to `number` but will trigger an error if the - value is too big to store in an int64. - - `string`: `sqlite3_result_text()` - - Uint8Array or Int8Array or ArrayBuffer: `sqlite3_result_blob()` - - `undefined`: is a no-op provided to simplify certain use cases. - - Anything else triggers `sqlite3_result_error()` with a - description of the problem. - - The first argument to this function is a `(sqlite3_context*)`. - Returns void. Does not throw. - */ - capi.sqlite3_result_js = function(pCtx, val) { - if (val instanceof Error) { - capi.sqlite3_result_error_js(pCtx, val); - return; - } - try { - switch (typeof val) { - case "undefined": break; - case "boolean": - capi.sqlite3_result_int(pCtx, val ? 1 : 0); - break; - case "bigint": - if (util.bigIntFits32(val)) capi.sqlite3_result_int(pCtx, Number(val)); - else if (util.bigIntFitsDouble(val)) capi.sqlite3_result_double(pCtx, Number(val)); - else if (wasm.bigIntEnabled) if (util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); - else toss3("BigInt value", val.toString(), "is too BigInt for int64."); - else toss3("BigInt value", val.toString(), "is too BigInt."); - break; - case "number": { - let f; - if (util.isInt32(val)) f = capi.sqlite3_result_int; - else if (wasm.bigIntEnabled && Number.isInteger(val) && util.bigIntFits64(BigInt(val))) f = capi.sqlite3_result_int64; - else f = capi.sqlite3_result_double; - f(pCtx, val); - break; - } - case "string": { - const [p, n] = wasm.allocCString(val, true); - capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC); - break; - } - case "object": if (null === val) { - capi.sqlite3_result_null(pCtx); - break; - } else if (util.isBindableTypedArray(val)) { - const pBlob = wasm.allocFromTypedArray(val); - capi.sqlite3_result_blob(pCtx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); - break; - } - default: toss3("Don't not how to handle this UDF result value:", typeof val, val); - } - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - /** - Returns the result sqlite3_column_value(pStmt,iCol) passed to - sqlite3_value_to_js(). The 3rd argument of this function is - ignored by this function except to pass it on as the second - argument of sqlite3_value_to_js(). If the sqlite3_column_value() - returns NULL (e.g. because the column index is out of range), - this function returns `undefined`, regardless of the 3rd - argument. If the 3rd argument is falsy and conversion fails, - `undefined` will be returned. - - Note that sqlite3_column_value() returns an "unprotected" value - object, but in a single-threaded environment (like this one) - there is no distinction between protected and unprotected values. - */ - capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert = true) { - const v = capi.sqlite3_column_value(pStmt, iCol); - return 0 === v ? void 0 : capi.sqlite3_value_to_js(v, throwIfCannotConvert); - }; - { - /** - Internal impl of sqlite3_preupdate_new/old_js() and - sqlite3changeset_new/old_js(). - */ - const __newOldValue = function(pObj, iCol, impl) { - impl = capi[impl]; - if (!this.ptr) this.ptr = wasm.allocPtr(); - else wasm.pokePtr(this.ptr, 0); - const rc = impl(pObj, iCol, this.ptr); - if (rc) return SQLite3Error.toss(rc, arguments[2] + "() failed with code " + rc); - const pv = wasm.peekPtr(this.ptr); - return pv ? capi.sqlite3_value_to_js(pv, true) : void 0; - }.bind(Object.create(null)); - /** - A wrapper around sqlite3_preupdate_new() which fetches the - sqlite3_value at the given index and returns the result of - passing it to sqlite3_value_to_js(). Throws on error. - */ - capi.sqlite3_preupdate_new_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_new"); - /** - The sqlite3_preupdate_old() counterpart of - sqlite3_preupdate_new_js(), with an identical interface. - */ - capi.sqlite3_preupdate_old_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_old"); - /** - A wrapper around sqlite3changeset_new() which fetches the - sqlite3_value at the given index and returns the result of - passing it to sqlite3_value_to_js(). Throws on error. - - If sqlite3changeset_new() succeeds but has no value to report, - this function returns the undefined value, noting that - undefined is not a valid conversion from an `sqlite3_value`, so - is unambiguous. - */ - capi.sqlite3changeset_new_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_new"); - /** - The sqlite3changeset_old() counterpart of - sqlite3changeset_new_js(), with an identical interface. - */ - capi.sqlite3changeset_old_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_old"); - } - const sqlite3 = { - WasmAllocError, - SQLite3Error, - capi, - util, - wasm, - config, - version: Object.create(null), - client: void 0, - asyncPostInit: async function ff() { - if (ff.isReady instanceof Promise) return ff.isReady; - let lia = this.initializersAsync; - delete this.initializersAsync; - const postInit = async () => { - if (!sqlite3.__isUnderTest) { - delete sqlite3.util; - delete sqlite3.StructBinder; - } - return sqlite3; - }; - const catcher = (e) => { - config.error("an async sqlite3 initializer failed:", e); - throw e; - }; - if (!lia || !lia.length) return ff.isReady = postInit().catch(catcher); - lia = lia.map((f) => { - return f instanceof Function ? async (x) => f(sqlite3) : f; - }); - lia.push(postInit); - let p = Promise.resolve(sqlite3); - while (lia.length) p = p.then(lia.shift()); - return ff.isReady = p.catch(catcher); - }.bind(sqlite3ApiBootstrap), - scriptInfo: void 0 - }; - if ("undefined" !== typeof sqlite3IsUnderTest) sqlite3.__isUnderTest = !!sqlite3IsUnderTest; - try { - sqlite3ApiBootstrap.initializers.forEach((f) => { - f(sqlite3); - }); - } catch (e) { - console.error("sqlite3 bootstrap initializer threw:", e); - throw e; - } - delete sqlite3ApiBootstrap.initializers; - sqlite3ApiBootstrap.sqlite3 = sqlite3; - if ("undefined" !== typeof sqlite3InitScriptInfo) { - sqlite3InitScriptInfo.debugModule("sqlite3ApiBootstrap() complete", sqlite3); - sqlite3.scriptInfo = sqlite3InitScriptInfo; - } - if (sqlite3.__isUnderTest) { - if ("undefined" !== typeof EmscriptenModule) sqlite3.config.emscripten = EmscriptenModule; - const iw = sqlite3.scriptInfo?.instantiateWasm; - if (iw) { - sqlite3.wasm.module = iw.module; - sqlite3.wasm.instance = iw.instance; - sqlite3.wasm.imports = iw.imports; - } - } - /** - Eliminate any confusion about whether these config objects may - be used after library initialization by eliminating the outward-facing - objects... - */ - delete globalThis.sqlite3ApiConfig; - delete globalThis.sqlite3ApiBootstrap; - delete sqlite3ApiBootstrap.defaultConfig; - return sqlite3.asyncPostInit().then((s) => { - if ("undefined" !== typeof sqlite3InitScriptInfo) sqlite3InitScriptInfo.debugModule("sqlite3.asyncPostInit() complete", s); - delete s.asyncPostInit; - delete s.scriptInfo; - delete s.emscripten; - return s; - }); - }; - /** - globalThis.sqlite3ApiBootstrap.initializers is an internal detail - used by the various pieces of the sqlite3 API's amalgamation - process. It must not be modified by client code except when plugging - such code into the amalgamation process. - - Each component of the amalgamation is expected to append a function - to this array. When sqlite3ApiBootstrap() is called for the first - time, each such function will be called (in their appended order) - and passed the sqlite3 namespace object, into which they can install - their features. At the end of that process, this array is deleted. - - The order of insertion into this array is significant for - some pieces. e.g. sqlite3.capi and sqlite3.wasm cannot be fully - utilized until the whwasmutil.js part is plugged in via - sqlite3-api-glue.js. - */ - globalThis.sqlite3ApiBootstrap.initializers = []; - /** - globalThis.sqlite3ApiBootstrap.initializersAsync is an internal detail - used by the sqlite3 API's amalgamation process. It must not be - modified by client code except when plugging such code into the - amalgamation process. - - The counterpart of globalThis.sqlite3ApiBootstrap.initializers, - specifically for initializers which are asynchronous. All entries in - this list must be either async functions, non-async functions which - return a Promise, or a Promise. Each function in the list is called - with the sqlite3 object as its only argument. - - The resolved value of any Promise is ignored and rejection will kill - the asyncPostInit() process (at an indeterminate point because all - of them are run asynchronously in parallel). - - This list is not processed until the client calls - sqlite3.asyncPostInit(). This means, for example, that intializers - added to globalThis.sqlite3ApiBootstrap.initializers may push entries to - this list. - */ - globalThis.sqlite3ApiBootstrap.initializersAsync = []; - /** - Client code may assign sqlite3ApiBootstrap.defaultConfig an - object-type value before calling sqlite3ApiBootstrap() (without - arguments) in order to tell that call to use this object as its - default config value. The intention of this is to provide - downstream clients with a reasonably flexible approach for plugging in - an environment-suitable configuration without having to define a new - global-scope symbol. - */ - globalThis.sqlite3ApiBootstrap.defaultConfig = Object.create(null); - /** - Placeholder: gets installed by the first call to - globalThis.sqlite3ApiBootstrap(). However, it is recommended that the - caller of sqlite3ApiBootstrap() capture its return value and delete - globalThis.sqlite3ApiBootstrap after calling it. It returns the same - value which will be stored here. - */ - globalThis.sqlite3ApiBootstrap.sqlite3 = void 0; - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - sqlite3.version = { - "libVersion": "3.52.0", - "libVersionNumber": 3052e3, - "sourceId": "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", - "downloadVersion": 352e4, - "scm": { - "sha3-256": "407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", - "branch": "trunk", - "tags": "", - "datetime": "2026-01-30T06:37:34.096Z" - } - }; - }); - globalThis.WhWasmUtilInstaller = function WhWasmUtilInstaller(target) { - "use strict"; - if (void 0 === target.bigIntEnabled) target.bigIntEnabled = !!globalThis["BigInt64Array"]; - /** Throws a new Error, the message of which is the concatenation of - all args with a space between each. */ - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - if (!target.pointerSize && !target.pointerIR && target.alloc && target.dealloc) { - const ptr = target.alloc(1); - target.pointerSize = "bigint" === typeof ptr ? 8 : 4; - target.dealloc(ptr); - } - /** - As of 2025-09-21, this library works with 64-bit WASM modules - built with Emscripten's -sMEMORY64=1. - */ - if (target.pointerSize && !target.pointerIR) target.pointerIR = 4 === target.pointerSize ? "i32" : "i64"; - const __ptrIR = target.pointerIR ??= "i32"; - const __ptrSize = target.pointerSize ??= "i32" === __ptrIR ? 4 : "i64" === __ptrIR ? 8 : 0; - delete target.pointerSize; - delete target.pointerIR; - if ("i32" !== __ptrIR && "i64" !== __ptrIR) toss("Invalid pointerIR:", __ptrIR); - else if (8 !== __ptrSize && 4 !== __ptrSize) toss("Invalid pointerSize:", __ptrSize); - /** Either BigInt or, if !target.bigIntEnabled, a function which - throws complaining that BigInt is not enabled. */ - const __BigInt = target.bigIntEnabled ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); - const __Number = (v) => Number(v || 0); - /** - If target.ptr.ir==='i32' then this is equivalent to - Number(v||0) else it's equivalent to BigInt(v||0), throwing - if BigInt support is disabled. - - Why? Because Number(null)===0, but BigInt(null) throws. We - perform the same for Number to allow the undefined value to be - treated as a NULL WASM pointer, primarily to reduce friction in - many SQLite3 bindings which have long relied on that. - */ - const __asPtrType = 4 === __ptrSize ? __Number : __BigInt; - /** - The number 0 as either type Number or BigInt, depending on - target.ptr.ir. - */ - const __NullPtr = __asPtrType(0); - /** - Expects any number of numeric arguments, each one of either type - Number or BigInt. It sums them up (from an implicit starting - point of 0 or 0n) and returns them as a number of the same type - which target.ptr.coerce() uses. - - This is a workaround for not being able to mix Number/BigInt in - addition/subtraction expressions (which we frequently need for - calculating pointer offsets). - */ - const __ptrAdd = function(...args) { - let rc = __asPtrType(0); - for (const v of args) rc += __asPtrType(v); - return rc; - }; - /** Set up target.ptr... */ - { - const __ptr = Object.create(null); - Object.defineProperty(target, "ptr", { - enumerable: true, - get: () => __ptr, - set: () => toss("The ptr property is read-only.") - }); - (function f(name, val) { - Object.defineProperty(__ptr, name, { - enumerable: true, - get: () => val, - set: () => toss("ptr[" + name + "] is read-only.") - }); - return f; - })("null", __NullPtr)("size", __ptrSize)("ir", __ptrIR)("coerce", __asPtrType)("add", __ptrAdd)("addn", 4 === __ptrIR ? __ptrAdd : (...args) => Number(__ptrAdd(...args))); - } - if (!target.exports) Object.defineProperty(target, "exports", { - enumerable: true, - configurable: true, - get: () => target.instance?.exports - }); - /** Stores various cached state. */ - const cache = Object.create(null); - /** Previously-recorded size of cache.memory.buffer, noted so that - we can recreate the view objects if the heap grows. */ - cache.heapSize = 0; - /** WebAssembly.Memory object extracted from target.memory or - target.exports.memory the first time heapWrappers() is - called. */ - cache.memory = null; - /** uninstallFunction() puts table indexes in here for reuse and - installFunction() extracts them. */ - cache.freeFuncIndexes = []; - /** - List-of-lists used by scopedAlloc() and friends. - */ - cache.scopedAlloc = []; - /** Push the pointer ptr to the current cache.scopedAlloc list - (which must already exist) and return ptr. */ - cache.scopedAlloc.pushPtr = (ptr) => { - cache.scopedAlloc[cache.scopedAlloc.length - 1].push(ptr); - return ptr; - }; - cache.utf8Decoder = new TextDecoder(); - cache.utf8Encoder = new TextEncoder("utf-8"); - /** - For the given IR-like string in the set ('i8', 'i16', 'i32', - 'f32', 'float', 'i64', 'f64', 'double', '*'), or any string value - ending in '*', returns the sizeof for that value - (target.ptr.size in the latter case). For any other value, it - returns the undefined value. - */ - target.sizeofIR = (n) => { - switch (n) { - case "i8": return 1; - case "i16": return 2; - case "i32": - case "f32": - case "float": return 4; - case "i64": - case "f64": - case "double": return 8; - case "*": return __ptrSize; - default: return ("" + n).endsWith("*") ? __ptrSize : void 0; - } - }; - /** - If (cache.heapSize !== cache.memory.buffer.byteLength), i.e. if - the heap has grown since the last call, updates cache.HEAPxyz. - Returns the cache object. - */ - const heapWrappers = function() { - if (!cache.memory) cache.memory = target.memory instanceof WebAssembly.Memory ? target.memory : target.exports.memory; - else if (cache.heapSize === cache.memory.buffer.byteLength) return cache; - const b = cache.memory.buffer; - cache.HEAP8 = new Int8Array(b); - cache.HEAP8U = new Uint8Array(b); - cache.HEAP16 = new Int16Array(b); - cache.HEAP16U = new Uint16Array(b); - cache.HEAP32 = new Int32Array(b); - cache.HEAP32U = new Uint32Array(b); - cache.HEAP32F = new Float32Array(b); - cache.HEAP64F = new Float64Array(b); - if (target.bigIntEnabled) if ("undefined" !== typeof BigInt64Array) { - cache.HEAP64 = new BigInt64Array(b); - cache.HEAP64U = new BigUint64Array(b); - } else toss("BigInt support is enabled, but the BigInt64Array type is missing."); - cache.heapSize = b.byteLength; - return cache; - }; - /** Convenience equivalent of this.heapForSize(8,false). */ - target.heap8 = () => heapWrappers().HEAP8; - /** Convenience equivalent of this.heapForSize(8,true). */ - target.heap8u = () => heapWrappers().HEAP8U; - /** Convenience equivalent of this.heapForSize(16,false). */ - target.heap16 = () => heapWrappers().HEAP16; - /** Convenience equivalent of this.heapForSize(16,true). */ - target.heap16u = () => heapWrappers().HEAP16U; - /** Convenience equivalent of this.heapForSize(32,false). */ - target.heap32 = () => heapWrappers().HEAP32; - /** Convenience equivalent of this.heapForSize(32,true). */ - target.heap32u = () => heapWrappers().HEAP32U; - /** - Requires n to be one of: - - - integer 8, 16, or 32. - - A integer-type TypedArray constructor: Int8Array, Int16Array, - Int32Array, or their Uint counterparts. - - If this.bigIntEnabled is true, it also accepts the value 64 or a - BigInt64Array/BigUint64Array, else it throws if passed 64 or one - of those constructors. - - Returns an integer-based TypedArray view of the WASM heap memory - buffer associated with the given block size. If passed an integer - as the first argument and unsigned is truthy then the "U" - (unsigned) variant of that view is returned, else the signed - variant is returned. If passed a TypedArray value, the 2nd - argument is ignored. Float32Array and Float64Array views are not - supported by this function. - - Growth of the heap will invalidate any references to this heap, - so do not hold a reference longer than needed and do not use a - reference after any operation which may allocate. Instead, - re-fetch the reference by calling this function again. - - Throws if passed an invalid n. - */ - target.heapForSize = function(n, unsigned = true) { - const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); - switch (n) { - case Int8Array: return c.HEAP8; - case Uint8Array: return c.HEAP8U; - case Int16Array: return c.HEAP16; - case Uint16Array: return c.HEAP16U; - case Int32Array: return c.HEAP32; - case Uint32Array: return c.HEAP32U; - case 8: return unsigned ? c.HEAP8U : c.HEAP8; - case 16: return unsigned ? c.HEAP16U : c.HEAP16; - case 32: return unsigned ? c.HEAP32U : c.HEAP32; - case 64: - if (c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64; - break; - default: if (target.bigIntEnabled) { - if (n === globalThis["BigUint64Array"]) return c.HEAP64U; - else if (n === globalThis["BigInt64Array"]) return c.HEAP64; - break; - } - } - toss("Invalid heapForSize() size: expecting 8, 16, 32,", "or (if BigInt is enabled) 64."); - }; - const __funcTable = target.functionTable; - delete target.functionTable; - /** - Returns the WASM-exported "indirect function table". - */ - target.functionTable = __funcTable ? () => __funcTable : () => target.exports.__indirect_function_table; - /** - Given a function pointer, returns the WASM function table entry - if found, else returns a falsy value: undefined if fptr is out of - range or null if it's in range but the table entry is empty. - */ - target.functionEntry = function(fptr) { - const ft = target.functionTable(); - return fptr < ft.length ? ft.get(__asPtrType(fptr)) : void 0; - }; - /** - Creates a WASM function which wraps the given JS function and - returns the JS binding of that WASM function. The signature - string must be the Jaccwabyt-format or Emscripten - addFunction()-format function signature string. In short: in may - have one of the following formats: - - - Emscripten: `"x..."`, where the first x is a letter representing - the result type and subsequent letters represent the argument - types. Functions with no arguments have only a single - letter. - - - Jaccwabyt: `"x(...)"` where `x` is the letter representing the - result type and letters in the parens (if any) represent the - argument types. Functions with no arguments use `x()`. - - Supported letters: - - - `i` = int32 - - `p` = int32 or int64 ("pointer"), depending on target.ptr.size - - `j` = int64 - - `f` = float32 - - `d` = float64 - - `v` = void, only legal for use as the result type - - It throws if an invalid signature letter is used. - - Jaccwabyt-format signatures support some additional letters which - have no special meaning here but (in this context) act as aliases - for other letters: - - - `s`, `P`: same as `p` - - Sidebar: this code is developed together with Jaccwabyt, thus the - support for its signature format. - - The arguments may be supplied in either order: (func,sig) or - (sig,func). - */ - target.jsFuncToWasm = function f(func, sig) { - /** Attribution: adapted up from Emscripten-generated glue code, - refactored primarily for efficiency's sake, eliminating - call-local functions and superfluous temporary arrays. */ - if (!f._) f._ = { - sigTypes: Object.assign(Object.create(null), { - i: "i32", - p: __ptrIR, - P: __ptrIR, - s: __ptrIR, - j: "i64", - f: "f32", - d: "f64" - }), - typeCodes: Object.assign(Object.create(null), { - f64: 124, - f32: 125, - i64: 126, - i32: 127 - }), - uleb128Encode: (tgt, method, n) => { - if (n < 128) tgt[method](n); - else tgt[method](n % 128 | 128, n >> 7); - }, - rxJSig: /^(\w)\((\w*)\)$/, - sigParams: (sig) => { - const m = f._.rxJSig.exec(sig); - return m ? m[2] : sig.substr(1); - }, - letterType: (x) => f._.sigTypes[x] || toss("Invalid signature letter:", x), - pushSigType: (dest, letter) => dest.push(f._.typeCodes[f._.letterType(letter)]) - }; - if ("string" === typeof func) { - const x = sig; - sig = func; - func = x; - } - const _ = f._; - const sigParams = _.sigParams(sig); - const wasmCode = [1, 96]; - _.uleb128Encode(wasmCode, "push", sigParams.length); - for (const x of sigParams) _.pushSigType(wasmCode, x); - if ("v" === sig[0]) wasmCode.push(0); - else { - wasmCode.push(1); - _.pushSigType(wasmCode, sig[0]); - } - _.uleb128Encode(wasmCode, "unshift", wasmCode.length); - wasmCode.unshift(0, 97, 115, 109, 1, 0, 0, 0, 1); - wasmCode.push(2, 7, 1, 1, 101, 1, 102, 0, 0, 7, 5, 1, 1, 102, 0, 0); - return new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(wasmCode)), { e: { f: func } }).exports["f"]; - }; - /** - Documented as target.installFunction() except for the 3rd - argument: if truthy, the newly-created function pointer - is stashed in the current scoped-alloc scope and will be - cleaned up at the matching scopedAllocPop(), else it - is not stashed there. - */ - const __installFunction = function f(func, sig, scoped) { - if (scoped && !cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); - if ("string" === typeof func) { - const x = sig; - sig = func; - func = x; - } - if ("string" !== typeof sig || !(func instanceof Function)) toss("Invalid arguments: expecting (function,signature) or (signature,function)."); - const ft = target.functionTable(); - const oldLen = __asPtrType(ft.length); - let ptr; - while (ptr = cache.freeFuncIndexes.pop()) if (ft.get(ptr)) { - ptr = null; - continue; - } else break; - if (!ptr) { - ptr = __asPtrType(oldLen); - ft.grow(__asPtrType(1)); - } - try { - ft.set(ptr, func); - if (scoped) cache.scopedAlloc.pushPtr(ptr); - return ptr; - } catch (e) { - if (!(e instanceof TypeError)) { - if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); - throw e; - } - } - try { - const fptr = target.jsFuncToWasm(func, sig); - ft.set(ptr, fptr); - if (scoped) cache.scopedAlloc.pushPtr(ptr); - } catch (e) { - if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); - throw e; - } - return ptr; - }; - /** - Expects a JS function and signature, exactly as for - this.jsFuncToWasm(). It uses that function to create a - WASM-exported function, installs that function to the next - available slot of this.functionTable(), and returns the - function's index in that table (which acts as a pointer to that - function). The returned pointer can be passed to - uninstallFunction() to uninstall it and free up the table slot - for reuse. - - If passed (string,function) arguments then it treats the first - argument as the signature and second as the function. - - As a special case, if the passed-in function is a WASM-exported - function then the signature argument is ignored and func is - installed as-is, without requiring re-compilation/re-wrapping. - - This function will propagate an exception if - WebAssembly.Table.grow() throws or this.jsFuncToWasm() throws. - The former case can happen in an Emscripten-compiled environment - when building without Emscripten's `-sALLOW_TABLE_GROWTH` flag. - - Sidebar: this function differs from Emscripten's addFunction() - _primarily_ in that it does not share that function's - undocumented behavior of reusing a function if it's passed to - addFunction() more than once, which leads to uninstallFunction() - breaking clients which do not take care to avoid that case: - - https://github.com/emscripten-core/emscripten/issues/17323 - */ - target.installFunction = (func, sig) => __installFunction(func, sig, false); - /** - Works exactly like installFunction() but requires that a - scopedAllocPush() is active and uninstalls the given function - when that alloc scope is popped via scopedAllocPop(). - This is used for implementing JS/WASM function bindings which - should only persist for the life of a call into a single - C-side function. - */ - target.scopedInstallFunction = (func, sig) => __installFunction(func, sig, true); - /** - Requires a pointer value previously returned from - this.installFunction(). Removes that function from the WASM - function table, marks its table slot as free for re-use, and - returns that function. It is illegal to call this before - installFunction() has been called and results are undefined if - ptr was not returned by that function. The returned function - may be passed back to installFunction() to reinstall it. - - To simplify certain use cases, if passed a falsy non-0 value - (noting that 0 is a valid function table index), this function - has no side effects and returns undefined. - */ - target.uninstallFunction = function(ptr) { - if (!ptr && __NullPtr !== ptr) return void 0; - const ft = target.functionTable(); - cache.freeFuncIndexes.push(ptr); - const rc = ft.get(ptr); - ft.set(ptr, null); - return rc; - }; - /** - Given a WASM heap memory address and a data type name in the form - (i8, i16, i32, i64, float (or f32), double (or f64)), this - fetches the numeric value from that address and returns it as a - number or, for the case of type='i64', a BigInt (with the caveat - BigInt will trigger an exception if this.bigIntEnabled is - falsy). Throws if given an invalid type. - - If the first argument is an array, it is treated as an array of - addresses and the result is an array of the values from each of - those address, using the same 2nd argument for determining the - value type to fetch. - - As a special case, if type ends with a `*`, it is considered to - be a pointer type and is treated as the WASM numeric type - appropriate for the pointer size (==this.ptr.ir). - - While possibly not obvious, this routine and its poke() - counterpart are how pointer-to-value _output_ parameters in - WASM-compiled C code can be interacted with: - - ``` - const ptr = alloc(4); - poke32(ptr, 0); // clear the ptr's value - aCFuncWithOutputPtrToInt32Arg(ptr); // e.g. void foo(int *x); - const result = peek32(ptr); // fetch ptr's value - dealloc(ptr); - ``` - - scopedAlloc() and friends can be used to make handling of - `ptr` safe against leaks in the case of an exception: - - ``` - let result; - const scope = scopedAllocPush(); - try{ - const ptr = scopedAlloc(4); - poke32(ptr, 0); - aCFuncWithOutputPtrArg(ptr); - result = peek32(ptr); - }finally{ - scopedAllocPop(scope); - } - ``` - - As a rule poke() must be called to set (typically zero out) the - pointer's value, else it will contain an essentially random - value. - - ACHTUNG: calling this often, e.g. in a loop, can have a noticably - painful impact on performance. Rather than doing so, use - heapForSize() to fetch the heap object and read directly from it. - - ACHTUNG #2: ptr may be a BigInt (and generally will be in 64-bit - builds) but this function must coerce it into a Number in order - to access the heap's contents. Ergo: BitInts outside of the - (extrardinarily genereous) address range exposed to browser-side - WASM may cause misbehavior. - - See also: poke() - */ - target.peek = function f(ptr, type = "i8") { - if (type.endsWith("*")) type = __ptrIR; - const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); - const list = Array.isArray(ptr) ? [] : void 0; - let rc; - do { - if (list) ptr = arguments[0].shift(); - switch (type) { - case "i1": - case "i8": - rc = c.HEAP8[Number(ptr) >> 0]; - break; - case "i16": - rc = c.HEAP16[Number(ptr) >> 1]; - break; - case "i32": - rc = c.HEAP32[Number(ptr) >> 2]; - break; - case "float": - case "f32": - rc = c.HEAP32F[Number(ptr) >> 2]; - break; - case "double": - case "f64": - rc = Number(c.HEAP64F[Number(ptr) >> 3]); - break; - case "i64": if (c.HEAP64) { - rc = __BigInt(c.HEAP64[Number(ptr) >> 3]); - break; - } - default: toss("Invalid type for peek():", type); - } - if (list) list.push(rc); - } while (list && arguments[0].length); - return list || rc; - }; - /** - The counterpart of peek(), this sets a numeric value at the given - WASM heap address, using the 3rd argument to define how many - bytes are written. Throws if given an invalid type. See peek() - for details about the `type` argument. If the 3rd argument ends - with `*` then it is treated as a pointer type and this function - behaves as if the 3rd argument were this.ptr.ir. - - If the first argument is an array, it is treated like a list - of pointers and the given value is written to each one. - - Returns `this`. (Prior to 2022-12-09 it returned this function.) - - ACHTUNG #1: see peek()'s ACHTUNG #1. - - ACHTUNG #2: see peek()'s ACHTUNG #2. - */ - target.poke = function(ptr, value, type = "i8") { - if (type.endsWith("*")) type = __ptrIR; - const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); - for (const p of Array.isArray(ptr) ? ptr : [ptr]) switch (type) { - case "i1": - case "i8": - c.HEAP8[Number(p) >> 0] = value; - continue; - case "i16": - c.HEAP16[Number(p) >> 1] = value; - continue; - case "i32": - c.HEAP32[Number(p) >> 2] = value; - continue; - case "float": - case "f32": - c.HEAP32F[Number(p) >> 2] = value; - continue; - case "double": - case "f64": - c.HEAP64F[Number(p) >> 3] = value; - continue; - case "i64": if (c.HEAP64) { - c.HEAP64[Number(p) >> 3] = __BigInt(value); - continue; - } - default: toss("Invalid type for poke(): " + type); - } - return this; - }; - /** - Convenience form of peek() intended for fetching - pointer-to-pointer values. If passed a single non-array argument - it returns the value of that one pointer address. If passed - multiple arguments, or a single array of arguments, it returns an - array of their values. - */ - target.peekPtr = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, __ptrIR); - /** - A variant of poke() intended for setting pointer-to-pointer - values. Its differences from poke() are that (1) it defaults to a - value of 0 and (2) it always writes to the pointer-sized heap - view. - */ - target.pokePtr = (ptr, value = 0) => target.poke(ptr, value, __ptrIR); - /** - Convenience form of peek() intended for fetching i8 values. If - passed a single non-array argument it returns the value of that - one pointer address. If passed multiple arguments, or a single - array of arguments, it returns an array of their values. - */ - target.peek8 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i8"); - /** - Convenience form of poke() intended for setting individual bytes. - Its difference from poke() is that it always writes to the - i8-sized heap view. - */ - target.poke8 = (ptr, value) => target.poke(ptr, value, "i8"); - /** i16 variant of peek8(). */ - target.peek16 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i16"); - /** i16 variant of poke8(). */ - target.poke16 = (ptr, value) => target.poke(ptr, value, "i16"); - /** i32 variant of peek8(). */ - target.peek32 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i32"); - /** i32 variant of poke8(). */ - target.poke32 = (ptr, value) => target.poke(ptr, value, "i32"); - /** i64 variant of peek8(). Will throw if this build is not - configured for BigInt support. */ - target.peek64 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i64"); - /** i64 variant of poke8(). Will throw if this build is not - configured for BigInt support. Note that this returns - a BigInt-type value, not a Number-type value. */ - target.poke64 = (ptr, value) => target.poke(ptr, value, "i64"); - /** f32 variant of peek8(). */ - target.peek32f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f32"); - /** f32 variant of poke8(). */ - target.poke32f = (ptr, value) => target.poke(ptr, value, "f32"); - /** f64 variant of peek8(). */ - target.peek64f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f64"); - /** f64 variant of poke8(). */ - target.poke64f = (ptr, value) => target.poke(ptr, value, "f64"); - /** Deprecated alias for getMemValue() */ - target.getMemValue = target.peek; - /** Deprecated alias for peekPtr() */ - target.getPtrValue = target.peekPtr; - /** Deprecated alias for poke() */ - target.setMemValue = target.poke; - /** Deprecated alias for pokePtr() */ - target.setPtrValue = target.pokePtr; - /** - Returns true if the given value appears to be legal for use as - a WASM pointer value. Its _range_ of values is not (cannot be) - validated except to ensure that it is a 32-bit integer with a - value of 0 or greater. Likewise, it cannot verify whether the - value actually refers to allocated memory in the WASM heap. - - Whether or not null or undefined are legal are context-dependent. - They generally are legal but this function does not treat them as - such because they're not strictly legal for passing as-is as WASM - integer arguments. - */ - target.isPtr32 = (ptr) => "number" === typeof ptr && ptr >= 0 && ptr === (ptr | 0); - /** 64-bit counterpart of isPtr32() and falls back to that function - if ptr is not a BigInt. */ - target.isPtr64 = (ptr) => "bigint" === typeof ptr ? ptr >= 0 : target.isPtr32(ptr); - /** - isPtr() is an alias for isPtr32() or isPtr64(), depending on the - value of target.ptr.size. - */ - target.isPtr = 4 === __ptrSize ? target.isPtr32 : target.isPtr64; - /** - Expects ptr to be a pointer into the WASM heap memory which - refers to a NUL-terminated C-style string encoded as UTF-8. - Returns the length, in bytes, of the string, as for `strlen(3)`. - As a special case, if !ptr or if it's not a pointer then it - returns `null`. Throws if ptr is out of range for - target.heap8u(). - */ - target.cstrlen = function(ptr) { - if (!ptr || !target.isPtr(ptr)) return null; - ptr = Number(ptr); - const h = heapWrappers().HEAP8U; - let pos = ptr; - for (; h[pos] !== 0; ++pos); - return pos - ptr; - }; - /** Internal helper to use in operations which need to distinguish - between TypedArrays which are backed by a SharedArrayBuffer - from those which are not. */ - const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; - /** Returns true if the given TypedArray object is backed by a - SharedArrayBuffer, else false. */ - const isSharedTypedArray = (aTypedArray) => aTypedArray.buffer instanceof __SAB; - target.isSharedTypedArray = isSharedTypedArray; - /** - Returns either aTypedArray.slice(begin,end) (if - aTypedArray.buffer is a SharedArrayBuffer) or - aTypedArray.subarray(begin,end) (if it's not). - - This distinction is important for APIs which don't like to - work on SABs, e.g. TextDecoder, and possibly for our - own APIs which work on memory ranges which "might" be - modified by other threads while they're working. - - begin and end may be of type Number or (in 64-bit builds) BigInt - (which get coerced to Numbers). - */ - const typedArrayPart = (aTypedArray, begin, end) => { - if (8 === __ptrSize) { - if ("bigint" === typeof begin) begin = Number(begin); - if ("bigint" === typeof end) end = Number(end); - } - return isSharedTypedArray(aTypedArray) ? aTypedArray.slice(begin, end) : aTypedArray.subarray(begin, end); - }; - target.typedArrayPart = typedArrayPart; - /** - Uses TextDecoder to decode the given half-open range of the given - TypedArray to a string. This differs from a simple call to - TextDecoder in that it accounts for whether the first argument is - backed by a SharedArrayBuffer or not, and can work more - efficiently if it's not (TextDecoder refuses to act upon an SAB). - - If begin/end are not provided or are falsy then each defaults to - the start/end of the array. - */ - const typedArrayToString = (typedArray, begin, end) => cache.utf8Decoder.decode(typedArrayPart(typedArray, begin, end)); - target.typedArrayToString = typedArrayToString; - /** - Expects ptr to be a pointer into the WASM heap memory which - refers to a NUL-terminated C-style string encoded as UTF-8. This - function counts its byte length using cstrlen() then returns a - JS-format string representing its contents. As a special case, if - ptr is falsy or not a pointer, `null` is returned. - */ - target.cstrToJs = function(ptr) { - const n = target.cstrlen(ptr); - return n ? typedArrayToString(heapWrappers().HEAP8U, Number(ptr), Number(ptr) + n) : null === n ? n : ""; - }; - /** - Given a JS string, this function returns its UTF-8 length in - bytes. Returns null if str is not a string. - */ - target.jstrlen = function(str) { - /** Attribution: derived from Emscripten's lengthBytesUTF8() */ - if ("string" !== typeof str) return null; - const n = str.length; - let len = 0; - for (let i = 0; i < n; ++i) { - let u = str.charCodeAt(i); - if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023; - if (u <= 127) ++len; - else if (u <= 2047) len += 2; - else if (u <= 65535) len += 3; - else len += 4; - } - return len; - }; - /** - Encodes the given JS string as UTF8 into the given TypedArray - tgt, starting at the given offset and writing, at most, maxBytes - bytes (including the NUL terminator if addNul is true, else no - NUL is added). If it writes any bytes at all and addNul is true, - it always NUL-terminates the output, even if doing so means that - the NUL byte is all that it writes. - - If maxBytes is negative (the default) then it is treated as the - remaining length of tgt, starting at the given offset. - - If writing the last character would surpass the maxBytes count - because the character is multi-byte, that character will not be - written (as opposed to writing a truncated multi-byte character). - This can lead to it writing as many as 3 fewer bytes than - maxBytes specifies. - - Returns the number of bytes written to the target, _including_ - the NUL terminator (if any). If it returns 0, it wrote nothing at - all, which can happen if: - - - str is empty and addNul is false. - - offset < 0. - - maxBytes == 0. - - maxBytes is less than the byte length of a multi-byte str[0]. - - Throws if tgt is not an Int8Array or Uint8Array. - - Design notes: - - - In C's strcpy(), the destination pointer is the first - argument. That is not the case here primarily because the 3rd+ - arguments are all referring to the destination, so it seems to - make sense to have them grouped with it. - - - Emscripten's counterpart of this function (stringToUTF8Array()) - returns the number of bytes written sans NUL terminator. That - is, however, ambiguous: str.length===0 or maxBytes===(0 or 1) - all cause 0 to be returned. - */ - target.jstrcpy = function(jstr, tgt, offset = 0, maxBytes = -1, addNul = true) { - /** Attribution: the encoding bits are taken from Emscripten's - stringToUTF8Array(). */ - if (!tgt || !(tgt instanceof Int8Array) && !(tgt instanceof Uint8Array)) toss("jstrcpy() target must be an Int8Array or Uint8Array."); - maxBytes = Number(maxBytes); - offset = Number(offset); - if (maxBytes < 0) maxBytes = tgt.length - offset; - if (!(maxBytes > 0) || !(offset >= 0)) return 0; - let i = 0, max = jstr.length; - const begin = offset, end = offset + maxBytes - (addNul ? 1 : 0); - for (; i < max && offset < end; ++i) { - let u = jstr.charCodeAt(i); - if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | jstr.charCodeAt(++i) & 1023; - if (u <= 127) { - if (offset >= end) break; - tgt[offset++] = u; - } else if (u <= 2047) { - if (offset + 1 >= end) break; - tgt[offset++] = 192 | u >> 6; - tgt[offset++] = 128 | u & 63; - } else if (u <= 65535) { - if (offset + 2 >= end) break; - tgt[offset++] = 224 | u >> 12; - tgt[offset++] = 128 | u >> 6 & 63; - tgt[offset++] = 128 | u & 63; - } else { - if (offset + 3 >= end) break; - tgt[offset++] = 240 | u >> 18; - tgt[offset++] = 128 | u >> 12 & 63; - tgt[offset++] = 128 | u >> 6 & 63; - tgt[offset++] = 128 | u & 63; - } - } - if (addNul) tgt[offset++] = 0; - return offset - begin; - }; - /** - Works similarly to C's strncpy(), copying, at most, n bytes (not - characters) from srcPtr to tgtPtr. It copies until n bytes have - been copied or a 0 byte is reached in src. _Unlike_ strncpy(), it - returns the number of bytes it assigns in tgtPtr, _including_ the - NUL byte (if any). If n is reached before a NUL byte in srcPtr, - tgtPtr will _not_ be NULL-terminated. If a NUL byte is reached - before n bytes are copied, tgtPtr will be NUL-terminated. - - If n is negative, cstrlen(srcPtr)+1 is used to calculate it, the - +1 being for the NUL byte. - - Throws if tgtPtr or srcPtr are falsy. Results are undefined if: - - - either is not a pointer into the WASM heap or - - - srcPtr is not NUL-terminated AND n is less than srcPtr's - logical length. - - ACHTUNG: it is possible to copy partial multi-byte characters - this way, and converting such strings back to JS strings will - have undefined results. - */ - target.cstrncpy = function(tgtPtr, srcPtr, n) { - if (!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings."); - if (n < 0) n = target.cstrlen(strPtr) + 1; - else if (!(n > 0)) return 0; - const heap = target.heap8u(); - let i = 0, ch; - const tgtNumber = Number(tgtPtr), srcNumber = Number(srcPtr); - for (; i < n && (ch = heap[srcNumber + i]); ++i) heap[tgtNumber + i] = ch; - if (i < n) heap[tgtNumber + i++] = 0; - return i; - }; - /** - For the given JS string, returns a Uint8Array of its contents - encoded as UTF-8. If addNul is true, the returned array will have - a trailing 0 entry, else it will not. - */ - target.jstrToUintArray = (str, addNul = false) => { - return cache.utf8Encoder.encode(addNul ? str + "\0" : str); - }; - const __affirmAlloc = (obj, funcName) => { - if (!(obj.alloc instanceof Function) || !(obj.dealloc instanceof Function)) toss("Object is missing alloc() and/or dealloc() function(s)", "required by", funcName + "()."); - }; - const __allocCStr = function(jstr, returnWithLength, allocator, funcName) { - __affirmAlloc(target, funcName); - if ("string" !== typeof jstr) return null; - const u = cache.utf8Encoder.encode(jstr), ptr = allocator(u.length + 1); - let toFree = ptr; - try { - const heap = heapWrappers().HEAP8U; - heap.set(u, Number(ptr)); - heap[__ptrAdd(ptr, u.length)] = 0; - toFree = __NullPtr; - return returnWithLength ? [ptr, u.length] : ptr; - } finally { - if (toFree) target.dealloc(toFree); - } - }; - /** - Uses target.alloc() to allocate enough memory for jstrlen(jstr)+1 - bytes of memory, copies jstr to that memory using jstrcpy(), - NUL-terminates it, and returns the pointer to that C-string. - Ownership of the pointer is transfered to the caller, who must - eventually pass the pointer to dealloc() to free it. - - If passed a truthy 2nd argument then its return semantics change: - it returns [ptr,n], where ptr is the C-string's pointer and n is - its cstrlen(). - - Throws if `target.alloc` or `target.dealloc` are not functions. - */ - target.allocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.alloc, "allocCString()"); - /** - Starts an "allocation scope." All allocations made using - scopedAlloc() are recorded in this scope and are freed when the - value returned from this function is passed to - scopedAllocPop(). - - This family of functions requires that the API's object have both - `alloc()` and `dealloc()` methods, else this function will throw. - - Intended usage: - - ``` - const scope = scopedAllocPush(); - try { - const ptr1 = scopedAlloc(100); - const ptr2 = scopedAlloc(200); - const ptr3 = scopedAlloc(300); - ... - // Note that only allocations made via scopedAlloc() - // are managed by this allocation scope. - }finally{ - scopedAllocPop(scope); - } - ``` - - The value returned by this function must be treated as opaque by - the caller, suitable _only_ for passing to scopedAllocPop(). - Its type and value are not part of this function's API and may - change in any given version of this code. - - `scopedAlloc.level` can be used to determine how many scoped - alloc levels are currently active. - */ - target.scopedAllocPush = function() { - __affirmAlloc(target, "scopedAllocPush"); - const a = []; - cache.scopedAlloc.push(a); - return a; - }; - /** - Cleans up all allocations made using scopedAlloc() in the context - of the given opaque state object, which must be a value returned - by scopedAllocPush(). See that function for an example of how to - use this function. It also uninstalls any WASM functions - installed with scopedInstallFunction(). - - Though scoped allocations are managed like a stack, this API - behaves properly if allocation scopes are popped in an order - other than the order they were pushed. The intent is that it - _always_ be used in a stack-like manner. - - If called with no arguments, it pops the most recent - scopedAllocPush() result: - - ``` - scopedAllocPush(); - try{ ... } finally { scopedAllocPop(); } - ``` - - It's generally recommended that it be passed an explicit argument - to help ensure that push/push are used in matching pairs, but in - trivial code that may be a non-issue. - */ - target.scopedAllocPop = function(state) { - __affirmAlloc(target, "scopedAllocPop"); - const n = arguments.length ? cache.scopedAlloc.indexOf(state) : cache.scopedAlloc.length - 1; - if (n < 0) toss("Invalid state object for scopedAllocPop()."); - if (0 === arguments.length) state = cache.scopedAlloc[n]; - cache.scopedAlloc.splice(n, 1); - for (let p; p = state.pop();) if (target.functionEntry(p)) target.uninstallFunction(p); - else target.dealloc(p); - }; - /** - Allocates n bytes of memory using this.alloc() and records that - fact in the state for the most recent call of scopedAllocPush(). - Ownership of the memory is given to scopedAllocPop(), which - will clean it up when it is called. The memory _must not_ be - passed to this.dealloc(). Throws if this API object is missing - the required `alloc()` or `dealloc()` functions or no scoped - alloc is active. - - See scopedAllocPush() for an example of how to use this function. - - The `level` property of this function can be queried to query how - many scoped allocation levels are currently active. - - See also: scopedAllocPtr(), scopedAllocCString() - */ - target.scopedAlloc = function(n) { - if (!cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); - const p = __asPtrType(target.alloc(n)); - return cache.scopedAlloc.pushPtr(p); - }; - Object.defineProperty(target.scopedAlloc, "level", { - configurable: false, - enumerable: false, - get: () => cache.scopedAlloc.length, - set: () => toss("The 'active' property is read-only.") - }); - /** - Works identically to allocCString() except that it allocates the - memory using scopedAlloc(). - - Will throw if no scopedAllocPush() call is active. - */ - target.scopedAllocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.scopedAlloc, "scopedAllocCString()"); - const __allocMainArgv = function(isScoped, list) { - const pList = target[isScoped ? "scopedAlloc" : "alloc"]((list.length + 1) * target.ptr.size); - let i = 0; - list.forEach((e) => { - target.pokePtr(__ptrAdd(pList, target.ptr.size * i++), target[isScoped ? "scopedAllocCString" : "allocCString"]("" + e)); - }); - target.pokePtr(__ptrAdd(pList, target.ptr.size * i), 0); - return pList; - }; - /** - Creates an array, using scopedAlloc(), suitable for passing to a - C-level main() routine. The input is a collection with a length - property and a forEach() method. A block of memory - (list.length+1) entries long is allocated and each pointer-sized - block of that memory is populated with a scopedAllocCString() - conversion of the (""+value) of each element, with the exception - that the final entry is a NULL pointer. Returns a pointer to the - start of the list, suitable for passing as the 2nd argument to a - C-style main() function. - - Throws if scopedAllocPush() is not active. - - Design note: the returned array is allocated with an extra NULL - pointer entry to accommodate certain APIs, but client code which - does not need that functionality should treat the returned array - as list.length entries long. - */ - target.scopedAllocMainArgv = (list) => __allocMainArgv(true, list); - /** - Identical to scopedAllocMainArgv() but uses alloc() instead of - scopedAlloc(). - */ - target.allocMainArgv = (list) => __allocMainArgv(false, list); - /** - Expects to be given a C-style string array and its length. It - returns a JS array of strings and/or nulls: any entry in the - pArgv array which is NULL results in a null entry in the result - array. If argc is 0 then an empty array is returned. - - Results are undefined if any entry in the first argc entries of - pArgv are neither 0 (NULL) nor legal UTF-format C strings. - - To be clear, the expected C-style arguments to be passed to this - function are `(int, char **)` (optionally const-qualified). - */ - target.cArgvToJs = (argc, pArgv) => { - const list = []; - for (let i = 0; i < argc; ++i) { - const arg = target.peekPtr(__ptrAdd(pArgv, target.ptr.size * i)); - list.push(arg ? target.cstrToJs(arg) : null); - } - return list; - }; - /** - Wraps function call func() in a scopedAllocPush() and - scopedAllocPop() block, such that all calls to scopedAlloc() and - friends from within that call will have their memory freed - automatically when func() returns. If func throws or propagates - an exception, the scope is still popped, otherwise it returns the - result of calling func(). - */ - target.scopedAllocCall = function(func) { - target.scopedAllocPush(); - try { - return func(); - } finally { - target.scopedAllocPop(); - } - }; - /** Internal impl for allocPtr() and scopedAllocPtr(). */ - const __allocPtr = function(howMany, safePtrSize, method) { - __affirmAlloc(target, method); - const pIr = safePtrSize ? "i64" : __ptrIR; - let m = target[method](howMany * (safePtrSize ? 8 : __ptrSize)); - target.poke(m, 0, pIr); - if (1 === howMany) return m; - const a = [m]; - for (let i = 1; i < howMany; ++i) { - m = __ptrAdd(m, safePtrSize ? 8 : __ptrSize); - a[i] = m; - target.poke(m, 0, pIr); - } - return a; - }; - /** - Allocates one or more pointers as a single chunk of memory and - zeroes them out. - - The first argument is the number of pointers to allocate. The - second specifies whether they should use a "safe" pointer size (8 - bytes) or whether they may use the default pointer size - (typically 4 but also possibly 8). - - How the result is returned depends on its first argument: if - passed 1, it returns the allocated memory address. If passed more - than one then an array of pointer addresses is returned, which - can optionally be used with "destructuring assignment" like this: - - ``` - const [p1, p2, p3] = allocPtr(3); - ``` - - ACHTUNG: when freeing the memory, pass only the _first_ result - value to dealloc(). The others are part of the same memory chunk - and must not be freed separately. - - The reason for the 2nd argument is... - - When one of the returned pointers will refer to a 64-bit value, - e.g. a double or int64, and that value must be written or fetched, - e.g. using poke() or peek(), it is important that - the pointer in question be aligned to an 8-byte boundary or else - it will not be fetched or written properly and will corrupt or - read neighboring memory. It is only safe to pass false when the - client code is certain that it will only get/fetch 4-byte values - (or smaller). - */ - target.allocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "alloc"); - /** - Identical to allocPtr() except that it allocates using scopedAlloc() - instead of alloc(). - */ - target.scopedAllocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "scopedAlloc"); - /** - If target.exports[name] exists, it is returned, else an - exception is thrown. - */ - target.xGet = function(name) { - return target.exports[name] || toss("Cannot find exported symbol:", name); - }; - const __argcMismatch = (f, n) => toss(f + "() requires", n, "argument(s)."); - /** - Looks up a WASM-exported function named fname from - target.exports. If found, it is called, passed all remaining - arguments, and its return value is returned to xCall's caller. If - not found, an exception is thrown. This function does no - conversion of argument or return types, but see xWrap() and - xCallWrapped() for variants which do. - - If the first argument is a function is is assumed to be - a WASM-bound function and is used as-is instead of looking up - the function via xGet(). - - As a special case, if passed only 1 argument after the name and - that argument in an Array, that array's entries become the - function arguments. (This is not an ambiguous case because it's - not legal to pass an Array object to a WASM function.) - */ - target.xCall = function(fname, ...args) { - const f = fname instanceof Function ? fname : target.xGet(fname); - if (!(f instanceof Function)) toss("Exported symbol", fname, "is not a function."); - if (f.length !== args.length) __argcMismatch(f === fname ? f.name : fname, f.length); - return 2 === arguments.length && Array.isArray(arguments[1]) ? f.apply(null, arguments[1]) : f.apply(null, args); - }; - /** - State for use with xWrap(). - */ - cache.xWrap = Object.create(null); - cache.xWrap.convert = Object.create(null); - /** Map of type names to argument conversion functions. */ - cache.xWrap.convert.arg = /* @__PURE__ */ new Map(); - /** Map of type names to return result conversion functions. */ - cache.xWrap.convert.result = /* @__PURE__ */ new Map(); - /** Scope-local convenience aliases. */ - const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result; - const __xArgPtr = __asPtrType; - xArg.set("i64", __BigInt).set("i32", (i) => i | 0).set("i16", (i) => (i | 0) & 65535).set("i8", (i) => (i | 0) & 255).set("f32", (i) => Number(i).valueOf()).set("float", xArg.get("f32")).set("f64", xArg.get("f32")).set("double", xArg.get("f64")).set("int", xArg.get("i32")).set("null", (i) => i).set(null, xArg.get("null")).set("**", __xArgPtr).set("*", __xArgPtr); - xResult.set("*", __xArgPtr).set("pointer", __xArgPtr).set("number", (v) => Number(v)).set("void", (v) => void 0).set(void 0, xResult.get("void")).set("null", (v) => v).set(null, xResult.get("null")); - for (const t of [ - "i8", - "i16", - "i32", - "i64", - "int", - "f32", - "float", - "f64", - "double" - ]) { - xArg.set(t + "*", __xArgPtr); - xResult.set(t + "*", __xArgPtr); - xResult.set(t, xArg.get(t) || toss("Maintenance required: missing arg converter for", t)); - } - /** - In order for args of type string to work in various contexts in - the sqlite3 API, we need to pass them on as, variably, a C-string - or a pointer value. Thus for ARGs of type 'string' and - '*'/'pointer' we behave differently depending on whether the - argument is a string or not: - - - If v is a string, scopeAlloc() a new C-string from it and return - that temp string's pointer. - - - Else return the value from the arg adapter defined for - target.ptr.ir. - - TODO? Permit an Int8Array/Uint8Array and convert it to a string? - Would that be too much magic concentrated in one place, ready to - backfire? We handle that at the client level in sqlite3 with a - custom argument converter. - */ - const __xArgString = (v) => { - return "string" === typeof v ? target.scopedAllocCString(v) : __asPtrType(v); - }; - xArg.set("string", __xArgString).set("utf8", __xArgString); - xResult.set("string", (i) => target.cstrToJs(i)).set("utf8", xResult.get("string")).set("string:dealloc", (i) => { - try { - return i ? target.cstrToJs(i) : null; - } finally { - target.dealloc(i); - } - }).set("utf8:dealloc", xResult.get("string:dealloc")).set("json", (i) => JSON.parse(target.cstrToJs(i))).set("json:dealloc", (i) => { - try { - return i ? JSON.parse(target.cstrToJs(i)) : null; - } finally { - target.dealloc(i); - } - }); - /** - Internal-use-only base class for FuncPtrAdapter and potentially - additional stateful argument adapter classes. - - Its main interface (convertArg()) is strictly internal, not to be - exposed to client code, as it may still need re-shaping. Only the - constructors of concrete subclasses should be exposed to clients, - and those in such a way that does not hinder internal redesign of - the convertArg() interface. - */ - const AbstractArgAdapter = class { - constructor(opt) { - this.name = opt.name || "unnamed adapter"; - } - /** - Gets called via xWrap() to "convert" v to whatever type - this specific class supports. - - argIndex is the argv index of _this_ argument in the - being-xWrap()'d call. argv is the current argument list - undergoing xWrap() argument conversion. argv entries to the - left of argIndex will have already undergone transformation and - those to the right will not have (they will have the values the - client-level code passed in, awaiting conversion). The RHS - indexes must never be relied upon for anything because their - types are indeterminate, whereas the LHS values will be - WASM-compatible values by the time this is called. - - The reason for the argv and argIndex arguments is that we - frequently need more context than v for a specific conversion, - and that context invariably lies in the LHS arguments of v. - Examples of how this is useful can be found in FuncPtrAdapter. - */ - convertArg(v, argv, argIndex) { - toss("AbstractArgAdapter must be subclassed."); - } - }; - /** - This type is recognized by xWrap() as a proxy for converting a JS - function to a C-side function, either permanently, for the - duration of a single call into the C layer, or semi-contextual, - where it may keep track of a single binding for a given context - and uninstall the binding if it's replaced. - - The constructor requires an options object with these properties: - - - name (optional): string describing the function binding. This - is solely for debugging and error-reporting purposes. If not - provided, an empty string is assumed. - - - signature: a function signature string compatible with - jsFuncToWasm(). - - - bindScope (string): one of ('transient', 'context', - 'singleton', 'permanent'). Bind scopes are: - - - 'transient': it will convert JS functions to WASM only for - the duration of the xWrap()'d function call, using - scopedInstallFunction(). Before that call returns, the - WASM-side binding will be uninstalled. - - - 'singleton': holds one function-pointer binding for this - instance. If it's called with a different function pointer, - it uninstalls the previous one after converting the new - value. This is only useful for use with "global" functions - which do not rely on any state other than this function - pointer. If the being-converted function pointer is intended - to be mapped to some sort of state object (e.g. an - `sqlite3*`) then "context" (see below) is the proper mode. - - - 'context': similar to singleton mode but for a given - "context", where the context is a key provided by the user - and possibly dependent on a small amount of call-time - context. This mode is the default if bindScope is _not_ set - but a property named contextKey (described below) is. - - - 'permanent': the function is installed and left there - forever. There is no way to recover its pointer address - later on for cleanup purposes. i.e. it effectively leaks. - - - callProxy (function): if set, this must be a function which - will act as a proxy for any "converted" JS function. It is - passed the being-converted function value and must return - either that function or a function which acts on its - behalf. The returned function will be the one which gets - installed into the WASM function table. The proxy must perform - any required argument conversion (it will be called from C - code, so will receive C-format arguments) before passing them - on to the being-converted function. Whether or not the proxy - itself must return a value depends on the context. If it does, - it must be a WASM-friendly value, as it will be returning from - a call made from WASM code. - - - contextKey (function): is only used if bindScope is 'context' - or if bindScope is not set and this function is, in which case - a bindScope of 'context' is assumed. This function gets bound - to this object, so its "this" is this object. It gets passed - (argv,argIndex), where argIndex is the index of _this_ function - in its _wrapping_ function's arguments, and argv is the - _current_ still-being-xWrap()-processed args array. (Got all - that?) When thisFunc(argv,argIndex) is called by xWrap(), all - arguments in argv to the left of argIndex will have been - processed by xWrap() by the time this is called. argv[argIndex] - will be the value the user passed in to the xWrap()'d function - for the argument this FuncPtrAdapter is mapped to. Arguments to - the right of argv[argIndex] will not yet have been converted - before this is called. The function must return a key which - uniquely identifies this function mapping context for _this_ - FuncPtrAdapter instance (other instances are not considered), - taking into account that C functions often take some sort of - state object as one or more of their arguments. As an example, - if the xWrap()'d function takes `(int,T*,functionPtr,X*)` then - this FuncPtrAdapter instance is argv[2], and contextKey(argv,2) - might return 'T@'+argv[1], or even just argv[1]. Note, - however, that the (`X*`) argument will not yet have been - processed by the time this is called and should not be used as - part of that key because its pre-conversion data type might be - unpredictable. Similarly, care must be taken with C-string-type - arguments: those to the left in argv will, when this is called, - be WASM pointers, whereas those to the right might (and likely - do) have another data type. When using C-strings in keys, never - use their pointers in the key because most C-strings in this - constellation are transient. Conversely, the pointer address - makes an ideal key for longer-lived native pointer types. - - Yes, that ^^^ is quite awkward, but it's what we have. In - context, as it were, it actually makes some sense, but one must - look under its hook a bit to understand why it's shaped the - way it is. - - The constructor only saves the above state for later, and does - not actually bind any functions. The conversion, if any, is - performed when its convertArg() method is called via xWrap(). - - Shortcomings: - - - These "reverse" bindings, i.e. calling into a JS-defined - function from a WASM-defined function (the generated proxy - wrapper), lack all type conversion support. That means, for - example, that... - - - Function pointers which include C-string arguments may still - need a level of hand-written wrappers around them, depending on - how they're used, in order to provide the client with JS - strings. Alternately, clients will need to perform such - conversions on their own, e.g. using cstrToJs(). The purpose of - the callProxy() method is to account for such cases. - */ - xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter { - constructor(opt) { - super(opt); - if (xArg.FuncPtrAdapter.warnOnUse) console.warn("xArg.FuncPtrAdapter is an internal-only API", "and is not intended to be invoked from", "client-level code. Invoked with:", opt); - this.name = opt.name || "unnamed"; - this.signature = opt.signature; - if (opt.contextKey instanceof Function) { - this.contextKey = opt.contextKey; - if (!opt.bindScope) opt.bindScope = "context"; - } - this.bindScope = opt.bindScope || toss("FuncPtrAdapter options requires a bindScope (explicit or implied)."); - if (FuncPtrAdapter.bindScopes.indexOf(opt.bindScope) < 0) toss("Invalid options.bindScope (" + opt.bindMod + ") for FuncPtrAdapter. Expecting one of: (" + FuncPtrAdapter.bindScopes.join(", ") + ")"); - this.isTransient = "transient" === this.bindScope; - this.isContext = "context" === this.bindScope; - this.isPermanent = "permanent" === this.bindScope; - this.singleton = "singleton" === this.bindScope ? [] : void 0; - this.callProxy = opt.callProxy instanceof Function ? opt.callProxy : void 0; - } - /** - The static class members are defined outside of the class to - work around an emcc toolchain build problem: one of the tools - in emsdk v3.1.42 does not support the static keyword. - */ - contextKey(argv, argIndex) { - return this; - } - /** - Returns this object's mapping for the given context key, in the - form of an an array, creating the mapping if needed. The key - may be anything suitable for use in a Map. - - The returned array is intended to be used as a pair of - [JSValue, WasmFuncPtr], where the first element is one passed - to this.convertArg() and the second is its WASM form. - */ - contextMap(key) { - const cm = this.__cmap || (this.__cmap = /* @__PURE__ */ new Map()); - let rc = cm.get(key); - if (void 0 === rc) cm.set(key, rc = []); - return rc; - } - /** - Gets called via xWrap() to "convert" v to a WASM-bound function - pointer. If v is one of (a WASM pointer, null, undefined) then - (v||0) is returned and any earlier function installed by this - mapping _might_, depending on how it's bound, be uninstalled. - If v is not one of those types, it must be a Function, for - which this method creates (if needed) a WASM function binding - and returns the WASM pointer to that binding. - - If this instance is not in 'transient' mode, it will remember - the binding for at least the next call, to avoid recreating the - function binding unnecessarily. - - If it's passed a pointer(ish) value for v, it assumes it's a - WASM function pointer and does _not_ perform any function - binding, so this object's bindMode is irrelevant/ignored for - such cases. - - See the parent class's convertArg() docs for details on what - exactly the 2nd and 3rd arguments are. - */ - convertArg(v, argv, argIndex) { - let pair = this.singleton; - if (!pair && this.isContext) pair = this.contextMap(this.contextKey(argv, argIndex)); - if (pair && 2 === pair.length && pair[0] === v) return pair[1]; - if (v instanceof Function) { - if (this.callProxy) v = this.callProxy(v); - const fp = __installFunction(v, this.signature, this.isTransient); - if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this, this.contextKey(argv, argIndex), "@" + fp, v); - if (pair) { - if (pair[1]) { - if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); - try { - cache.scopedAlloc.pushPtr(pair[1]); - } catch (e) {} - } - pair[0] = arguments[0] || __NullPtr; - pair[1] = fp; - } - return fp; - } else if (target.isPtr(v) || null === v || void 0 === v) { - if (pair && pair[1] && pair[1] !== v) { - if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); - try { - cache.scopedAlloc.pushPtr(pair[1]); - } catch (e) {} - pair[0] = pair[1] = v || __NullPtr; - } - return v || __NullPtr; - } else throw new TypeError("Invalid FuncPtrAdapter argument type. Expecting a function pointer or a " + (this.name ? this.name + " " : "") + "function matching signature " + this.signature + "."); - } - }; - /** If true, the constructor emits a warning. The intent is that - this be set to true after bootstrapping of the higher-level - client library is complete, to warn downstream clients that - they shouldn't be relying on this implementation detail which - does not have a stable interface. */ - xArg.FuncPtrAdapter.warnOnUse = false; - /** If true, convertArg() will call FuncPtrAdapter.debugOut() when - it (un)installs a function binding to/from WASM. Note that - deinstallation of bindScope=transient bindings happens via - scopedAllocPop() so will not be output. */ - xArg.FuncPtrAdapter.debugFuncInstall = false; - /** Function used for debug output. */ - xArg.FuncPtrAdapter.debugOut = console.debug.bind(console); - /** - List of legal values for the FuncPtrAdapter bindScope config - option. - */ - xArg.FuncPtrAdapter.bindScopes = [ - "transient", - "context", - "singleton", - "permanent" - ]; - /** Throws if xArg.get(t) returns falsy. */ - const __xArgAdapterCheck = (t) => xArg.get(t) || toss("Argument adapter not found:", t); - /** Throws if xResult.get(t) returns falsy. */ - const __xResultAdapterCheck = (t) => xResult.get(t) || toss("Result adapter not found:", t); - /** - Fetches the xWrap() argument adapter mapped to t, calls it, - passing in all remaining arguments, and returns the result. - Throws if t is not mapped to an argument converter. - */ - cache.xWrap.convertArg = (t, ...args) => __xArgAdapterCheck(t)(...args); - /** - Identical to convertArg() except that it does not perform - an is-defined check on the mapping to t before invoking it. - */ - cache.xWrap.convertArgNoCheck = (t, ...args) => xArg.get(t)(...args); - /** - Fetches the xWrap() result adapter mapped to t, calls it, passing - it v, and returns the result. Throws if t is not mapped to an - argument converter. - */ - cache.xWrap.convertResult = (t, v) => null === t ? v : t ? __xResultAdapterCheck(t)(v) : void 0; - /** - Identical to convertResult() except that it does not perform an - is-defined check on the mapping to t before invoking it. - */ - cache.xWrap.convertResultNoCheck = (t, v) => null === t ? v : t ? xResult.get(t)(v) : void 0; - /** - Creates a wrapper for another function which converts the arguments - of the wrapper to argument types accepted by the wrapped function, - then converts the wrapped function's result to another form - for the wrapper. - - The first argument must be one of: - - - A JavaScript function. - - The name of a WASM-exported function. xGet() is used to fetch - the exported function, which throws if it's not found. - - A pointer into the indirect function table. e.g. a pointer - returned from target.installFunction(). - - It returns either the passed-in function or a wrapper for that - function which converts the JS-side argument types into WASM-side - types and converts the result type. - - The second argument, `resultType`, describes the conversion for - the wrapped functions result. A literal `null` or the string - `'null'` both mean to return the original function's value as-is - (mnemonic: there is "null" conversion going on). Literal - `undefined` or the string `"void"` both mean to ignore the - function's result and return `undefined`. Aside from those two - special cases, the `resultType` value may be one of the values - described below or any mapping installed by the client using - xWrap.resultAdapter(). - - If passed 3 arguments and the final one is an array, that array - must contain a list of type names (see below) for adapting the - arguments from JS to WASM. If passed 2 arguments, more than 3, - or the 3rd is not an array, all arguments after the 2nd (if any) - are treated as type names. i.e.: - - ``` - xWrap('funcname', 'i32', 'string', 'f64'); - // is equivalent to: - xWrap('funcname', 'i32', ['string', 'f64']); - ``` - - This function enforces that the given list of arguments has the - same arity as the being-wrapped function (as defined by its - `length` property) and it will throw if that is not the case. - Similarly, the created wrapper will throw if passed a differing - argument count. The intent of that strictness is to help catch - coding errors in using JS-bound WASM functions earlier rather - than laer. - - Type names are symbolic names which map the arguments to an - adapter function to convert, if needed, the value before passing - it on to WASM or to convert a return result from WASM. The list - of pre-defined names: - - - `i8`, `i16`, `i32` (args and results): all integer conversions - which convert their argument to an integer and truncate it to - the given bit length. - - - `*`, `**`, and `pointer` (args): are assumed to be WASM pointer - values and are returned coerced to an appropriately-sized - pointer value (i32 or i64). Non-numeric values will coerce to 0 - and out-of-range values will have undefined results (just as - with any pointer misuse). - - - `*` and `pointer` (results): aliases for the current - WASM pointer numeric type. - - - `**` (args): is simply a descriptive alias for the WASM pointer - type. It's primarily intended to mark output-pointer arguments, - noting that JS's view of WASM does not distinguish between - pointers and pointers-to-pointers, so all such interpretation - of `**`, as distinct from `*`, necessarily happens at the - client level. - - - `NumType*` (args): a type name in this form, where T is - the name of a numeric mapping, e.g. 'int16' or 'double', - is treated like `*`. - - - `i64` (args and results): passes the value to BigInt() to - convert it to an int64. This conversion will if bigIntEnabled - is falsy. - - - `f32` (`float`), `f64` (`double`) (args and results): pass - their argument to Number(). i.e. the adapter does not currently - distinguish between the two types of floating-point numbers. - - - `number` (results): converts the result to a JS Number using - Number(theValue). This is for result conversions only, as it's - not possible to generically know which type of number to - convert arguments to. - - Non-numeric conversions include: - - - `null` literal or `"null"` string (args and results): perform - no translation and pass the arg on as-is. This is primarily - useful for results but may have a use or two for arguments. - - - `string` or `utf8` (args): has two different semantics in order - to accommodate various uses of certain C APIs - (e.g. output-style strings)... - - - If the arg is a JS string, it creates a _temporary_ - UTF-8-encoded C-string to pass to the exported function, - cleaning it up before the wrapper returns. If a long-lived - C-string pointer is required, that requires client-side code - to create the string then pass its pointer to the function. - - - Else the arg is assumed to be a pointer to a string the - client has already allocated and it's passed on as - a WASM pointer. - - - `string` or `utf8` (results): treats the result value as a - const C-string, encoded as UTF-8, copies it to a JS string, - and returns that JS string. - - - `string:dealloc` or `utf8:dealloc` (results): treats the result - value as a non-const UTF-8 C-string, ownership of which has - just been transfered to the caller. It copies the C-string to a - JS string, frees the C-string using dealloc(), and returns the - JS string. If such a result value is NULL, the JS result is - `null`. Achtung: when using an API which returns results from a - specific allocator, e.g. `my_malloc()`, this conversion _is not - legal_. Instead, an equivalent conversion which uses the - appropriate deallocator is required. For example: - - ```js - target.xWrap.resultAdapter('string:my_free',(i)=>{ - try { return i ? target.cstrToJs(i) : null; } - finally{ target.exports.my_free(i); } - }; - ``` - - - `json` (results): treats the result as a const C-string and - returns the result of passing the converted-to-JS string to - JSON.parse(). Returns `null` if the C-string is a NULL pointer. - - - `json:dealloc` (results): works exactly like `string:dealloc` but - returns the same thing as the `json` adapter. Note the - warning in `string:dealloc` regarding matching allocators and - deallocators. - - The type names for results and arguments are validated when - xWrap() is called and any unknown names will trigger an - exception. - - Clients may map their own result and argument adapters using - xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all - type conversions are valid for both arguments _and_ result types - as they often have different memory ownership requirements. - - Design note: the ability to pass in a JS function as the first - argument is of relatively limited use, primarily for testing - argument and result converters. JS functions, by and large, will - not want to deal with C-type arguments. - - TODOs: - - - Figure out how/whether we can (semi-)transparently handle - pointer-type _output_ arguments. Those currently require - explicit handling by allocating pointers, assigning them before - the call using poke(), and fetching them with - peek() after the call. We may be able to automate some - or all of that. - - - Figure out whether it makes sense to extend the arg adapter - interface such that each arg adapter gets an array containing - the results of the previous arguments in the current call. That - might allow some interesting type-conversion feature. Use case: - handling of the final argument to sqlite3_prepare_v2() depends - on the type (pointer vs JS string) of its 2nd - argument. Currently that distinction requires hand-writing a - wrapper for that function. That case is unusual enough that - abstracting it into this API (and taking on the associated - costs) may well not make good sense. - */ - target.xWrap = function callee(fArg, resultType, ...argTypes) { - if (3 === arguments.length && Array.isArray(arguments[2])) argTypes = arguments[2]; - if (target.isPtr(fArg)) fArg = target.functionEntry(fArg) || toss("Function pointer not found in WASM function table."); - const fIsFunc = fArg instanceof Function; - const xf = fIsFunc ? fArg : target.xGet(fArg); - if (fIsFunc) fArg = xf.name || "unnamed function"; - if (argTypes.length !== xf.length) __argcMismatch(fArg, xf.length); - if (0 === xf.length && (null === resultType || "null" === resultType)) return xf; - __xResultAdapterCheck(resultType); - for (const t of argTypes) if (t instanceof AbstractArgAdapter) xArg.set(t, (...args) => t.convertArg(...args)); - else __xArgAdapterCheck(t); - const cxw = cache.xWrap; - if (0 === xf.length) return (...args) => args.length ? __argcMismatch(fArg, xf.length) : cxw.convertResult(resultType, xf.call(null)); - return function(...args) { - if (args.length !== xf.length) __argcMismatch(fArg, xf.length); - const scope = target.scopedAllocPush(); - try { - let i = 0; - if (callee.debug) console.debug("xWrap() preparing: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); - for (; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], args, i); - if (callee.debug) console.debug("xWrap() calling: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); - return cxw.convertResultNoCheck(resultType, xf.apply(null, args)); - } finally { - target.scopedAllocPop(scope); - } - }; - }; - /** - Internal impl for xWrap.resultAdapter() and argAdapter(). - - func = one of xWrap.resultAdapter or xWrap.argAdapter. - - argc = the number of args in the wrapping call to this - function. - - typeName = the first arg to the wrapping function. - - adapter = the second arg to the wrapping function. - - modeName = a descriptive name of the wrapping function for - error-reporting purposes. - - xcvPart = one of xResult or xArg. - - This acts as either a getter (if 1===argc) or setter (if - 2===argc) for the given adapter. Returns func on success or - throws if (A) called with 2 args but adapter is-not-a Function or - (B) typeName is not a string or (C) argc is not one of (1, 2). - */ - const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart) { - if ("string" === typeof typeName) { - if (1 === argc) return xcvPart.get(typeName); - else if (2 === argc) { - if (!adapter) { - xcvPart.delete(typeName); - return func; - } else if (!(adapter instanceof Function)) toss(modeName, "requires a function argument."); - xcvPart.set(typeName, adapter); - return func; - } - } - toss("Invalid arguments to", modeName); - }; - /** - Gets, sets, or removes a result value adapter for use with - xWrap(). If passed only 1 argument, the adapter function for the - given type name is returned. If the second argument is explicit - falsy (as opposed to defaulted), the adapter named by the first - argument is removed. If the 2nd argument is not falsy, it must be - a function which takes one value and returns a value appropriate - for the given type name. The adapter may throw if its argument is - not of a type it can work with. This function throws for invalid - arguments. - - Example: - - ``` - xWrap.resultAdapter('twice',(v)=>v+v); - ``` - - Result adapters MUST NOT use the scopedAlloc() family of APIs to - allocate a result value. xWrap()-generated wrappers run in the - context of scopedAllocPush() so that argument adapters can easily - convert, e.g., to C-strings, and have them cleaned up - automatically before the wrapper returns to the caller. Likewise, - if a _result_ adapter uses scoped allocation, the result will be - freed before the wrapper returns, leading to chaos and undefined - behavior. - - When called as a setter, this function returns itself. - */ - target.xWrap.resultAdapter = function f(typeName, adapter) { - return __xAdapter(f, arguments.length, typeName, adapter, "resultAdapter()", xResult); - }; - /** - Functions identically to xWrap.resultAdapter() but applies to - call argument conversions instead of result value conversions. - - xWrap()-generated wrappers perform argument conversion in the - context of a scopedAllocPush(), so any memory allocation - performed by argument adapters really, really, really should be - made using the scopedAlloc() family of functions unless - specifically necessary. For example: - - ``` - xWrap.argAdapter('my-string', function(v){ - return ('string'===typeof v) - ? myWasmObj.scopedAllocCString(v) : null; - }; - ``` - - Contrariwise, _result_ adapters _must not_ use scopedAlloc() to - allocate results because they would be freed before the - xWrap()-created wrapper returns. - - It is perfectly legitimate to use these adapters to perform - argument validation, as opposed (or in addition) to conversion. - When used that way, they should throw for invalid arguments. - */ - target.xWrap.argAdapter = function f(typeName, adapter) { - return __xAdapter(f, arguments.length, typeName, adapter, "argAdapter()", xArg); - }; - target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter; - /** - Functions like xCall() but performs argument and result type - conversions as for xWrap(). The first, second, and third - arguments are as documented for xWrap(), except that the 3rd - argument may be either a falsy value or empty array to represent - nullary functions. The 4th+ arguments are arguments for the call, - with the special case that if the 4th argument is an array, it is - used as the arguments for the call. Returns the converted result - of the call. - - This is just a thin wrapper around xWrap(). If the given function - is to be called more than once, it's more efficient to use - xWrap() to create a wrapper, then to call that wrapper as many - times as needed. For one-shot calls, however, this variant is - simpler. - */ - target.xCallWrapped = function(fArg, resultType, argTypes, ...args) { - if (Array.isArray(arguments[3])) args = arguments[3]; - return target.xWrap(fArg, resultType, argTypes || []).apply(null, args || []); - }; - /** - This function is ONLY exposed in the public API to facilitate - testing. It should not be used in application-level code, only - in test code. - - Expects to be given (typeName, value) and returns a conversion - of that value as has been registered using argAdapter(). - It throws if no adapter is found. - - ACHTUNG: the adapter may require that a scopedAllocPush() is - active and it may allocate memory within that scope. It may also - require additional arguments, depending on the type of - conversion. - */ - target.xWrap.testConvertArg = cache.xWrap.convertArg; - /** - This function is ONLY exposed in the public API to facilitate - testing. It should not be used in application-level code, only - in test code. - - Expects to be given (typeName, value) and returns a conversion - of that value as has been registered using resultAdapter(). - It throws if no adapter is found. - - ACHTUNG: the adapter may allocate memory which the caller may need - to know how to free. - */ - target.xWrap.testConvertResult = cache.xWrap.convertResult; - return target; - }; - /** - yawl (Yet Another Wasm Loader) provides very basic wasm loader. - It requires a config object: - - - `uri`: required URI of the WASM file to load. - - - `onload(loadResult)`: optional callback. Its argument is an - object described in more detail below. - - - `imports`: optional imports object for - WebAssembly.instantiate[Streaming](). The default is an empty - set of imports. If the module requires any imports, this object - must include them. - - - `wasmUtilTarget`: optional object suitable for passing to - WhWasmUtilInstaller(). If set, it gets passed to that function - before the returned promise resolves. This function sets several - properties on it before passing it on to that function (which - sets many more): - - - `module`, `instance`: the properties from the - instantiate[Streaming]() result. - - - If `instance.exports.memory` is _not_ set then it requires that - `config.imports.env.memory` be set (else it throws), and - assigns that to `wasmUtilTarget.memory`. - - - If `wasmUtilTarget.alloc` is not set and - `instance.exports.malloc` is, it installs - `wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()` - wrappers for the exports' `malloc` and `free` functions - if exports.malloc exists. - - It returns a function which, when called, initiates loading of the - module and returns a Promise. When that Promise resolves, it calls - the `config.onload` callback (if set) and passes it `(loadResult)`, - where `loadResult` is derived from the result of - WebAssembly.instantiate[Streaming](), an object in the form: - - ``` - { - module: a WebAssembly.Module, - instance: a WebAssembly.Instance, - config: the config arg to this function - } - ``` - - (The initial `then()` attached to the promise gets only that - object, and not the `config` object, thus the potential need for a - `config.onload` handler.) - - Error handling is up to the caller, who may attach a `catch()` call - to the promise. - */ - globalThis.WhWasmUtilInstaller.yawl = function yawl(config) { - "use strict"; - const wfetch = () => fetch(config.uri, { credentials: "same-origin" }); - const wui = this; - const finalThen = function(arg) { - if (config.wasmUtilTarget) { - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - const tgt = config.wasmUtilTarget; - tgt.module = arg.module; - tgt.instance = arg.instance; - if (!tgt.instance.exports.memory) - /** - WhWasmUtilInstaller requires either tgt.exports.memory - (exported from WASM) or tgt.memory (JS-provided memory - imported into WASM). - */ - tgt.memory = config?.imports?.env?.memory || toss("Missing 'memory' object!"); - if (!tgt.alloc && arg.instance.exports.malloc) { - const exports = arg.instance.exports; - tgt.alloc = function(n) { - return exports.malloc(n) || toss("Allocation of", n, "bytes failed."); - }; - tgt.dealloc = function(m) { - m && exports.free(m); - }; - } - wui(tgt); - } - arg.config = config; - if (config.onload) config.onload(arg); - return arg; - }; - return WebAssembly.instantiateStreaming ? () => WebAssembly.instantiateStreaming(wfetch(), config.imports || {}).then(finalThen) : () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, config.imports || {})).then(finalThen); - }.bind(globalThis.WhWasmUtilInstaller); - globalThis.Jaccwabyt = function StructBinderFactory(config) { - "use strict"; - /** Throws a new Error, the message of which is the concatenation - all args with a space between each. */ - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - { - let h = config.heap; - if (h instanceof WebAssembly.Memory) h = function() { - return new Uint8Array(this.buffer); - }.bind(h); - else if (!(h instanceof Function)) toss("config.heap must be WebAssembly.Memory instance or", "a function which returns one."); - config.heap = h; - } - ["alloc", "dealloc"].forEach(function(k) { - config[k] instanceof Function || toss("Config option '" + k + "' must be a function."); - }); - const SBF = StructBinderFactory, heap = config.heap, alloc = config.alloc, dealloc = config.dealloc; - config.realloc; - const log = config.log || console.debug.bind(console), memberPrefix = config.memberPrefix || "", memberSuffix = config.memberSuffix || "", BigInt = globalThis["BigInt"], BigInt64Array = globalThis["BigInt64Array"], bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array; - let ptr; - const ptrSize = config.pointerSize || config.ptrSize || ("bigint" === typeof (ptr = alloc(1)) ? 8 : 4); - const ptrIR = config.pointerIR || config.ptrIR || (4 === ptrSize ? "i32" : "i64"); - if (ptr) { - dealloc(ptr); - ptr = void 0; - } - if (ptrSize !== 4 && ptrSize !== 8) toss("Invalid pointer size:", ptrSize); - if (ptrIR !== "i32" && ptrIR !== "i64") toss("Invalid pointer representation:", ptrIR); - /** Either BigInt or, if !bigIntEnabled, a function which - throws complaining that BigInt is not enabled. */ - const __BigInt = bigIntEnabled && BigInt ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); - const __asPtrType = "i32" == ptrIR ? Number : __BigInt; - const __NullPtr = __asPtrType(0); - /** - Expects any number of numeric arguments, each one of either type - Number or BigInt. It sums them up (from an implicit starting - point of 0 or 0n) and returns them as a number of the same type - which target.asPtrType() uses. - - This is a workaround for not being able to mix Number/BigInt in - addition/subtraction expressions (which we frequently need for - calculating pointer offsets). - */ - const __ptrAdd = function(...args) { - let rc = __NullPtr; - for (let i = 0; i < args.length; ++i) rc += __asPtrType(args[i]); - return rc; - }; - const __ptrAddSelf = function(...args) { - return __ptrAdd(this.pointer, ...args); - }; - if (!SBF.debugFlags) { - SBF.__makeDebugFlags = function(deriveFrom = null) { - if (deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags; - const f = function f(flags) { - if (0 === arguments.length) return f.__flags; - if (flags < 0) { - delete f.__flags.getter; - delete f.__flags.setter; - delete f.__flags.alloc; - delete f.__flags.dealloc; - } else { - f.__flags.getter = 0 !== (1 & flags); - f.__flags.setter = 0 !== (2 & flags); - f.__flags.alloc = 0 !== (4 & flags); - f.__flags.dealloc = 0 !== (8 & flags); - } - return f._flags; - }; - Object.defineProperty(f, "__flags", { - iterable: false, - writable: false, - value: Object.create(deriveFrom) - }); - if (!deriveFrom) f(0); - return f; - }; - SBF.debugFlags = SBF.__makeDebugFlags(); - } - const isLittleEndian = true; - /** - Some terms used in the internal docs: - - StructType: a struct-wrapping class generated by this - framework. - - DEF: struct description object. - - SIG: struct member signature string. - */ - /** True if SIG s looks like a function signature, else - false. */ - const isFuncSig = (s) => "(" === s[1]; - /** True if SIG s is-a pointer-type signature. */ - const isPtrSig = (s) => "p" === s || "P" === s || "s" === s; - const isAutoPtrSig = (s) => "P" === s; - /** Returns p if SIG s is a function SIG, else returns s[0]. */ - const sigLetter = (s) => s ? isFuncSig(s) ? "p" : s[0] : void 0; - /** Returns the WASM IR form of the letter at SIG s[0]. Throws for - an unknown SIG. */ - const sigIR = function(s) { - switch (sigLetter(s)) { - case "c": - case "C": return "i8"; - case "i": return "i32"; - case "p": - case "P": - case "s": return ptrIR; - case "j": return "i64"; - case "f": return "float"; - case "d": return "double"; - } - toss("Unhandled signature IR:", s); - }; - /** Returns the WASM sizeof of the letter at SIG s[0]. Throws for an - unknown SIG. */ - const sigSize = function(s) { - switch (sigLetter(s)) { - case "c": - case "C": return 1; - case "i": return 4; - case "p": - case "P": - case "s": return ptrSize; - case "j": return 8; - case "f": return 4; - case "d": return 8; - } - toss("Unhandled signature sizeof:", s); - }; - const affirmBigIntArray = BigInt64Array ? () => true : () => toss("BigInt64Array is not available."); - /** Returns the name of a DataView getter method corresponding - to the given SIG. */ - const sigDVGetter = function(s) { - switch (sigLetter(s)) { - case "p": - case "P": - case "s": - switch (ptrSize) { - case 4: return "getInt32"; - case 8: return affirmBigIntArray() && "getBigInt64"; - } - break; - case "i": return "getInt32"; - case "c": return "getInt8"; - case "C": return "getUint8"; - case "j": return affirmBigIntArray() && "getBigInt64"; - case "f": return "getFloat32"; - case "d": return "getFloat64"; - } - toss("Unhandled DataView getter for signature:", s); - }; - /** Returns the name of a DataView setter method corresponding - to the given SIG. */ - const sigDVSetter = function(s) { - switch (sigLetter(s)) { - case "p": - case "P": - case "s": - switch (ptrSize) { - case 4: return "setInt32"; - case 8: return affirmBigIntArray() && "setBigInt64"; - } - break; - case "i": return "setInt32"; - case "c": return "setInt8"; - case "C": return "setUint8"; - case "j": return affirmBigIntArray() && "setBigInt64"; - case "f": return "setFloat32"; - case "d": return "setFloat64"; - } - toss("Unhandled DataView setter for signature:", s); - }; - /** - Returns a factory for either Number or BigInt, depending on the - given SIG. This constructor is used in property setters to coerce - the being-set value to the correct pointer size. - */ - const sigDVSetWrapper = function(s) { - switch (sigLetter(s)) { - case "i": - case "f": - case "c": - case "C": - case "d": return Number; - case "j": return __BigInt; - case "p": - case "P": - case "s": - switch (ptrSize) { - case 4: return Number; - case 8: return __BigInt; - } - break; - } - toss("Unhandled DataView set wrapper for signature:", s); - }; - /** Returns the given struct and member name in a form suitable for - debugging and error output. */ - const sPropName = (s, k) => s + "::" + k; - const __propThrowOnSet = function(structName, propName) { - return () => toss(sPropName(structName, propName), "is read-only."); - }; - /** - In order to completely hide StructBinder-bound struct pointers - from JS code, we store them in a scope-local WeakMap which maps - the struct-bound objects to an object with their metadata: - - { - .p = the native pointer, - .o = self (for an eventual reverse-mapping), - .xb = extra bytes allocated for p, - .zod = zeroOnDispose, - .ownsPointer = true if this object owns p - } - - The .p data are accessible via obj.pointer, which is gated behind - a property interceptor, but are not exposed anywhere else in the - public API. - */ - const getInstanceHandle = function f(obj, create = true) { - let ii = f.map.get(obj); - if (!ii && create) f.map.set(obj, ii = f.create(obj)); - return ii; - }; - getInstanceHandle.map = /* @__PURE__ */ new WeakMap(); - getInstanceHandle.create = (forObj) => { - return Object.assign(Object.create(null), { - o: forObj, - p: void 0, - ownsPointer: false, - zod: false, - xb: 0 - }); - }; - /** - Remove the getInstanceHandle() mapping for obj. - */ - const rmInstanceHandle = (obj) => getInstanceHandle.map.delete(obj); - const __isPtr32 = (ptr) => "number" === typeof ptr && ptr === (ptr | 0) && ptr >= 0; - const __isPtr64 = (ptr) => "bigint" === typeof ptr && ptr >= 0 || __isPtr32(ptr); - /** - isPtr() is an alias for isPtr32() or isPtr64(), depending on - ptrSize. - */ - const __isPtr = 4 === ptrSize ? __isPtr32 : __isPtr64; - const __isNonNullPtr = (v) => __isPtr(v) && v > 0; - /** Frees the obj.pointer memory (a.k.a. m), handles obj.ondispose, - and unmaps obj from its native resources. */ - const __freeStruct = function(ctor, obj, m) { - const ii = getInstanceHandle(obj, false); - if (!ii) return; - rmInstanceHandle(obj); - if (!m && !(m = ii.p)) { - console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo"); - return; - } - if (Array.isArray(obj.ondispose)) { - let x; - while (x = obj.ondispose.pop()) try { - if (x instanceof Function) x.call(obj); - else if (x instanceof StructType) x.dispose(); - else if (__isPtr(x)) dealloc(x); - } catch (e) { - console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); - } - } else if (obj.ondispose instanceof Function) try { - obj.ondispose(); - } catch (e) { - console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); - } - delete obj.ondispose; - if (ctor.debugFlags.__flags.dealloc) log("debug.dealloc:", ii.ownsPointer ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + m); - if (ii.ownsPointer) { - if (ii.zod || ctor.structInfo.zeroOnDispose) heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof + ii.xb); - dealloc(m); - } - }; - /** Returns a skeleton for a read-only, non-iterable property - * descriptor. */ - const rop0 = () => { - return { - configurable: false, - writable: false, - iterable: false - }; - }; - /** Returns a skeleton for a read-only property accessor wrapping - value v. */ - const rop = (v) => { - return { - ...rop0(), - value: v - }; - }; - /** Allocates obj's memory buffer based on the size defined in - ctor.structInfo.sizeof. */ - const __allocStruct = function f(ctor, obj, xm) { - let opt; - const checkPtr = (ptr) => { - __isNonNullPtr(ptr) || toss("Invalid pointer value", arguments[0], "for", ctor.structName, "constructor."); - }; - if (arguments.length >= 3) if (xm && "object" === typeof xm) { - opt = xm; - xm = opt?.wrap; - } else { - checkPtr(xm); - opt = { wrap: xm }; - } - else opt = {}; - const fill = !xm; - let nAlloc = 0; - let ownsPointer = false; - if (xm) { - checkPtr(xm); - ownsPointer = !!opt?.takeOwnership; - } else { - const nX = opt?.extraBytes ?? 0; - if (nX < 0 || nX !== (nX | 0)) toss("Invalid extraBytes value:", opt?.extraBytes); - nAlloc = ctor.structInfo.sizeof + nX; - xm = alloc(nAlloc) || toss("Allocation of", ctor.structName, "structure failed."); - ownsPointer = true; - } - try { - if (opt?.debugFlags) obj.debugFlags(opt.debugFlags); - if (ctor.debugFlags.__flags.alloc) log("debug.alloc:", fill ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + xm); - if (fill) heap().fill(0, Number(xm), Number(xm) + nAlloc); - const ii = getInstanceHandle(obj); - ii.p = xm; - ii.ownsPointer = ownsPointer; - ii.xb = nAlloc ? nAlloc - ctor.structInfo.sizeof : 0; - ii.zod = !!opt?.zeroOnDispose; - if (opt?.ondispose && opt.ondispose !== xm) obj.addOnDispose(opt.ondispose); - } catch (e) { - __freeStruct(ctor, obj, xm); - throw e; - } - }; - /** True if sig looks like an emscripten/jaccwabyt - type signature, else false. */ - const looksLikeASig = function f(sig) { - f.rxSig1 ??= /^[ipPsjfdcC]$/; - f.rxSig2 ??= /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/; - return f.rxSig1.test(sig) || f.rxSig2.test(sig); - }; - /** Returns a pair of adaptor maps (objects) in a length-3 - array specific to the given object. */ - const __adaptorsFor = function(who) { - let x = this.get(who); - if (!x) { - x = [ - Object.create(null), - Object.create(null), - Object.create(null) - ]; - this.set(who, x); - } - return x; - }.bind(/* @__PURE__ */ new WeakMap()); - /** Code de-duplifier for __adaptGet(), __adaptSet(), and - __adaptStruct(). */ - const __adaptor = function(who, which, key, proxy) { - const a = __adaptorsFor(who)[which]; - if (3 === arguments.length) return a[key]; - if (proxy) return a[key] = proxy; - return delete a[key]; - }; - const __adaptGet = function(key, ...args) { - return __adaptor(this, 0, key, ...args); - }; - const __affirmNotASig = function(ctx, key) { - looksLikeASig(key) && toss(ctx, "(", key, ") collides with a data type signature."); - }; - const __adaptSet = function(key, ...args) { - __affirmNotASig("Setter adaptor", key); - return __adaptor(this, 1, key, ...args); - }; - const __adaptStruct = function(key, ...args) { - __affirmNotASig("Struct adaptor", key); - return __adaptor(this, 2, key, ...args); - }; - /** - An internal counterpart of __adaptStruct(). If key is-a string, - uses __adaptor(who) to fetch the struct-adaptor entry for key, - else key is assumed to be a struct description object. If it - resolves to an object, that's returned, else an exception is - thrown. - */ - const __adaptStruct2 = function(who, key) { - const si = "string" === typeof key ? __adaptor(who, 2, key) : key; - if ("object" !== typeof si) toss("Invalid struct mapping object. Arg =", key, JSON.stringify(si)); - return si; - }; - const __memberKey = (k) => memberPrefix + k + memberSuffix; - const __memberKeyProp = rop(__memberKey); - /** - Looks up a struct member in structInfo.members. Throws if found - if tossIfNotFound is true, else returns undefined if not - found. The given name may be either the name of the - structInfo.members key (faster) or the key as modified by the - memberPrefix and memberSuffix settings. - */ - const __lookupMember = function(structInfo, memberName, tossIfNotFound = true) { - let m = structInfo.members[memberName]; - if (!m && (memberPrefix || memberSuffix)) { - for (const v of Object.values(structInfo.members)) if (v.key === memberName) { - m = v; - break; - } - if (!m && tossIfNotFound) toss(sPropName(structInfo.name || structInfo.structName, memberName), "is not a mapped struct member."); - } - return m; - }; - /** - Uses __lookupMember(obj.structInfo,memberName) to find a member, - throwing if not found. Returns its signature, either in this - framework's native format or in Emscripten format. - */ - const __memberSignature = function f(obj, memberName, emscriptenFormat = false) { - if (!f._) f._ = (x) => x.replace(/[^vipPsjrdcC]/g, "").replace(/[pPscC]/g, "i"); - const m = __lookupMember(obj.structInfo, memberName, true); - return emscriptenFormat ? f._(m.signature) : m.signature; - }; - /** Impl of X.memberKeys() for StructType and struct ctors. */ - const __structMemberKeys = rop(function() { - const a = []; - for (const k of Object.keys(this.structInfo.members)) a.push(this.memberKey(k)); - return a; - }); - const __utf8Decoder = new TextDecoder("utf-8"); - const __utf8Encoder = new TextEncoder(); - /** Internal helper to use in operations which need to distinguish - between SharedArrayBuffer heap memory and non-shared heap. */ - const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; - const __utf8Decode = function(arrayBuffer, begin, end) { - if (8 === ptrSize) { - begin = Number(begin); - end = Number(end); - } - return __utf8Decoder.decode(arrayBuffer.buffer instanceof __SAB ? arrayBuffer.slice(begin, end) : arrayBuffer.subarray(begin, end)); - }; - /** - Uses __lookupMember() to find the given obj.structInfo key. - Returns that member if it is a string, else returns false. If the - member is not found, throws if tossIfNotFound is true, else - returns false. - */ - const __memberIsString = function(obj, memberName, tossIfNotFound = false) { - const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound); - return m && 1 === m.signature.length && "s" === m.signature[0] ? m : false; - }; - /** - Given a member description object, throws if member.signature is - not valid for assigning to or interpretation as a C-style string. - It optimistically assumes that any signature of (i,p,s) is - C-string compatible. - */ - const __affirmCStringSignature = function(member) { - if ("s" === member.signature) return; - toss("Invalid member type signature for C-string value:", JSON.stringify(member)); - }; - /** - Looks up the given member in obj.structInfo. If it has a - signature of 's' then it is assumed to be a C-style UTF-8 string - and a decoded copy of the string at its address is returned. If - the signature is of any other type, it throws. If an s-type - member's address is 0, `null` is returned. - */ - const __memberToJsString = function f(obj, memberName) { - const m = __lookupMember(obj.structInfo, memberName, true); - __affirmCStringSignature(m); - const addr = obj[m.key]; - if (!addr) return null; - let pos = addr; - const mem = heap(); - for (; mem[pos] !== 0; ++pos); - return addr === pos ? "" : __utf8Decode(mem, addr, pos); - }; - /** - Adds value v to obj.ondispose, creating ondispose, - or converting it to an array, if needed. - */ - const __addOnDispose = function(obj, ...v) { - if (obj.ondispose) { - if (!Array.isArray(obj.ondispose)) obj.ondispose = [obj.ondispose]; - } else obj.ondispose = []; - obj.ondispose.push(...v); - }; - /** - Allocates a new UTF-8-encoded, NUL-terminated copy of the given - JS string and returns its address relative to heap(). If - allocation returns 0 this function throws. Ownership of the - memory is transfered to the caller, who must eventually pass it - to the configured dealloc() function. - */ - const __allocCString = function(str) { - const u = __utf8Encoder.encode(str); - const mem = alloc(u.length + 1); - if (!mem) toss("Allocation error while duplicating string:", str); - const h = heap(); - h.set(u, Number(mem)); - h[__ptrAdd(mem, u.length)] = 0; - return mem; - }; - /** - Sets the given struct member of obj to a dynamically-allocated, - UTF-8-encoded, NUL-terminated copy of str. It is up to the caller - to free any prior memory, if appropriate. The newly-allocated - string is added to obj.ondispose so will be freed when the object - is disposed. - - The given name may be either the name of the structInfo.members - key (faster) or the key as modified by the memberPrefix and - memberSuffix settings. - */ - const __setMemberCString = function(obj, memberName, str) { - const m = __lookupMember(obj.structInfo, memberName, true); - __affirmCStringSignature(m); - const mem = __allocCString(str); - obj[m.key] = mem; - __addOnDispose(obj, mem); - return obj; - }; - /** - Prototype for all StructFactory instances (the constructors - returned from StructBinder). - */ - const StructType = function StructType(structName, structInfo) { - if (arguments[2] !== rop) toss("Do not call the StructType constructor", "from client-level code."); - Object.defineProperties(this, { - structName: rop(structName), - structInfo: rop(structInfo) - }); - }; - /** - Properties inherited by struct-type-specific StructType instances - and (indirectly) concrete struct-type instances. - */ - StructType.prototype = Object.create(null, { - dispose: rop(function() { - __freeStruct(this.constructor, this); - }), - lookupMember: rop(function(memberName, tossIfNotFound = true) { - return __lookupMember(this.structInfo, memberName, tossIfNotFound); - }), - memberToJsString: rop(function(memberName) { - return __memberToJsString(this, memberName); - }), - memberIsString: rop(function(memberName, tossIfNotFound = true) { - return __memberIsString(this, memberName, tossIfNotFound); - }), - memberKey: __memberKeyProp, - memberKeys: __structMemberKeys, - memberSignature: rop(function(memberName, emscriptenFormat = false) { - return __memberSignature(this, memberName, emscriptenFormat); - }), - memoryDump: rop(function() { - const p = this.pointer; - return p ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof)) : null; - }), - extraBytes: { - configurable: false, - enumerable: false, - get: function() { - return getInstanceHandle(this, false)?.xb ?? 0; - } - }, - zeroOnDispose: { - configurable: false, - enumerable: false, - get: function() { - return getInstanceHandle(this, false)?.zod ?? !!this.structInfo.zeroOnDispose; - } - }, - pointer: { - configurable: false, - enumerable: false, - get: function() { - return getInstanceHandle(this, false)?.p; - }, - set: () => toss("Cannot assign the 'pointer' property of a struct.") - }, - setMemberCString: rop(function(memberName, str) { - return __setMemberCString(this, memberName, str); - }) - }); - Object.assign(StructType.prototype, { addOnDispose: function(...v) { - __addOnDispose(this, ...v); - return this; - } }); - /** - "Static" properties for StructType. - */ - Object.defineProperties(StructType, { - allocCString: rop(__allocCString), - isA: rop((v) => v instanceof StructType), - hasExternalPointer: rop((v) => { - const ii = getInstanceHandle(v, false); - return !!(ii?.p && !ii?.ownsPointer); - }), - memberKey: __memberKeyProp - }); - /** - If struct description object si has a getter proxy, return it (a - function), else return undefined. - */ - const memberGetterProxy = function(si) { - return si.get || (si.adaptGet ? StructBinder.adaptGet(si.adaptGet) : void 0); - }; - /** - If struct description object si has a setter proxy, return it (a - function), else return undefined. - */ - const memberSetterProxy = function(si) { - return si.set || (si.adaptSet ? StructBinder.adaptSet(si.adaptSet) : void 0); - }; - /** - To be called by makeMemberWrapper() when si has a 'members' - member, i.e. is an embedded struct. This function sets up that - struct like any other and also sets up property accessor for - ctor.memberKey(name) which returns an instance of that new - StructType when the member is accessed. That instance wraps the - memory of the member's part of the containing C struct instance. - - That is, if struct Foo has member bar which is an inner struct - then: - - const f = new Foo; - const b = f.bar; - assert( b is-a StructType object ); - assert( b.pointer === f.b.pointer ); - - b will be disposed of when f() is. Calling b.dispose() will not - do any permanent harm, as the wrapper object will be recreated - when accessing f.bar, pointing to the same memory in f. - - The si.zeroOnDispose flag has no effect on embedded structs because - they wrap "external" memory, so do not own it, and are thus never - freed, as such. - */ - const makeMemberStructWrapper = function callee(ctor, name, si) { - /** - Where we store inner-struct member proxies. Keys are a - combination of the parent object's pointer address and the - property's name. The values are StructType instances. - */ - const __innerStructs = callee.innerStructs ??= /* @__PURE__ */ new Map(); - const key = ctor.memberKey(name); - if (void 0 !== si.signature) toss("'signature' cannot be used on an embedded struct (", ctor.structName, ".", key, ")."); - if (memberSetterProxy(si)) toss("'set' and 'adaptSet' are not permitted for nested struct members."); - si.structName ??= ctor.structName + "::" + name; - si.key = key; - si.name = name; - si.constructor = this.call(this, si.structName, si); - const getterProxy = memberGetterProxy(si); - const prop = Object.assign(Object.create(null), { - configurable: false, - enumerable: false, - set: __propThrowOnSet(ctor.structName, key), - get: function() { - const dbg = this.debugFlags.__flags; - const p = this.pointer; - const k = p + "." + key; - let s = __innerStructs.get(k); - if (dbg.getter) log("debug.getter: k =", k); - if (!s) { - s = new si.constructor(__ptrAdd(p, si.offset)); - __innerStructs.set(k, s); - this.addOnDispose(() => s.dispose()); - s.addOnDispose(() => __innerStructs.delete(k)); - } - if (getterProxy) s = getterProxy.apply(this, [s, key]); - if (dbg.getter) log("debug.getter: result =", s); - return s; - } - }); - Object.defineProperty(ctor.prototype, key, prop); - }; - /** - This is where most of the magic happens. - - Pass this a StructBinderImpl-generated constructor, a member - property name, and the struct member description object. It will - define property accessors for proto[memberKey] which read - from/write to memory in this.pointer. It modifies si to make - certain downstream operations simpler. - */ - const makeMemberWrapper = function f(ctor, name, si) { - si = __adaptStruct2(this, si); - if (si.members) return makeMemberStructWrapper.call(this, ctor, name, si); - if (!f.cache) { - f.cache = { - getters: {}, - setters: {}, - sw: {} - }; - const a = [ - "i", - "c", - "C", - "p", - "P", - "s", - "f", - "d", - "v()" - ]; - if (bigIntEnabled) a.push("j"); - a.forEach(function(v) { - f.cache.getters[v] = sigDVGetter(v); - f.cache.setters[v] = sigDVSetter(v); - f.cache.sw[v] = sigDVSetWrapper(v); - }); - f.sigCheck = function(obj, name, key, sig) { - if (Object.prototype.hasOwnProperty.call(obj, key)) toss(obj.structName, "already has a property named", key + "."); - looksLikeASig(sig) || toss("Malformed signature for", sPropName(obj.structName, name) + ":", sig); - }; - } - const key = ctor.memberKey(name); - f.sigCheck(ctor.prototype, name, key, si.signature); - si.key = key; - si.name = name; - const sigGlyph = sigLetter(si.signature); - const xPropName = sPropName(ctor.structName, key); - const dbg = ctor.debugFlags.__flags; - const getterProxy = memberGetterProxy(si); - const prop = Object.create(null); - prop.configurable = false; - prop.enumerable = false; - prop.get = function() { - /** - This getter proxy reads its value from the appropriate pointer - address in the heap. It knows where and how much to read based on - this.pointer, si.offset, and si.sizeof. - */ - if (dbg.getter) log("debug.getter:", f.cache.getters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof); - let rc = new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.getters[sigGlyph]](0, isLittleEndian); - if (getterProxy) rc = getterProxy.apply(this, [key, rc]); - if (dbg.getter) log("debug.getter:", xPropName, "result =", rc); - return rc; - }; - if (si.readOnly) prop.set = __propThrowOnSet(ctor.prototype.structName, key); - else { - const setterProxy = memberSetterProxy(si); - prop.set = function(v) { - /** - The converse of prop.get(), this encodes v into the appropriate - spot in the WASM heap. - */ - if (dbg.setter) log("debug.setter:", f.cache.setters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof, v); - if (!this.pointer) toss("Cannot set native property on a disposed", this.structName, "instance."); - if (setterProxy) v = setterProxy.apply(this, [key, v]); - if (null === v || void 0 === v) v = __NullPtr; - else if (isPtrSig(si.signature) && !__isPtr(v)) if (isAutoPtrSig(si.signature) && v instanceof StructType) { - v = v.pointer || __NullPtr; - if (dbg.setter) log("debug.setter:", xPropName, "resolved to", v); - } else toss("Invalid value for pointer-type", xPropName + "."); - new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian); - }; - } - Object.defineProperty(ctor.prototype, key, prop); - }; - /** - The main factory function which will be returned to the - caller. The third argument is structly for internal use. - - This level of indirection is to avoid that clients can pass a - third argument to this, as that's only for internal use. - - internalOpt options: - - - None right now. This is for potential use in recursion. - - Usages: - - StructBinder(string, obj [,optObj]); - StructBinder(obj); - */ - const StructBinderImpl = function StructBinderImpl(structName, si, opt = Object.create(null)) { - /** - StructCtor is the eventual return value of this function. We - need to populate this early on so that we can do some trickery - in feeding it through recursion. - - Uses: - - // heap-allocated: - const x = new StructCtor; - // externally-managed memory: - const y = new StructCtor( aPtrToACompatibleCStruct ); - - or, more recently: - - const z = new StructCtor({ - extraBytes: [int=0] extra bytes to allocate after the struct - - wrap: [aPtrToACompatibleCStruct=undefined]. If provided, this - instance waps, but does not (by default) own the memory, else - a new instance is allocated from the WASM heap. - - ownsPointer: true if this object takes over ownership of - wrap. - - zeroOnDispose: [bool=StructCtor.structInfo.zeroOnDispose] - - autoCalcSizeOffset: [bool=false] Automatically calculate - sizeof an offset. This is fine for pure-JS structs (which - probably aren't useful beyond testing of Jaccwabyt) but it's - dangerous to use with actual WASM objects because we cannot - be guaranteed to have the same memory layout as an ostensibly - matching C struct. This applies recursively to all children - of the struct description. - - // TODO? Per-instance overrides of the struct-level flags? - - get: (k,v)=>v, - set: (k,v)=>v, - adaptGet: string, - adaptSet: string - - // That wouldn't fit really well right now, apparently. - }); - - */ - const StructCtor = function StructCtor(arg) { - if (!(this instanceof StructCtor)) toss("The", structName, "constructor may only be called via 'new'."); - __allocStruct(StructCtor, this, ...arguments); - }; - const self = this; - /** - "Convert" struct description x to a struct description, if - needed. This expands adaptStruct() mappings and transforms - {memberName:signatureString} signature syntax to object form. - */ - const ads = (x) => { - return "string" === typeof x && looksLikeASig(x) ? { signature: x } : __adaptStruct2(self, x); - }; - if (1 === arguments.length) { - si = ads(structName); - structName = si.structName || si.name; - } else if (2 === arguments.length) { - si = ads(si); - si.name ??= structName; - } else si = ads(si); - structName ??= si.structName; - structName ??= opt.structName; - if (!structName) toss("One of 'name' or 'structName' are required."); - if (si.adapt) { - Object.keys(si.adapt.struct || {}).forEach((k) => { - __adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]); - }); - Object.keys(si.adapt.set || {}).forEach((k) => { - __adaptSet.call(StructBinderImpl, k, si.adapt.set[k]); - }); - Object.keys(si.adapt.get || {}).forEach((k) => { - __adaptGet.call(StructBinderImpl, k, si.adapt.get[k]); - }); - } - if (!si.members && !si.sizeof) si.sizeof = sigSize(si.signature); - const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags)); - Object.defineProperties(StructCtor, { - debugFlags, - isA: rop((v) => v instanceof StructCtor), - memberKey: __memberKeyProp, - memberKeys: __structMemberKeys, - structInfo: rop(si), - structName: rop(structName), - ptrAdd: rop(__ptrAdd) - }); - StructCtor.prototype = new StructType(structName, si, rop); - Object.defineProperties(StructCtor.prototype, { - debugFlags, - constructor: rop(StructCtor), - ptrAdd: rop(__ptrAddSelf) - }); - let lastMember = false; - let offset = 0; - const autoCalc = !!si.autoCalcSizeOffset; - if (!autoCalc) { - if (!si.sizeof) toss(structName, "description is missing its sizeof property."); - si.offset ??= 0; - } else si.offset ??= 0; - Object.keys(si.members || {}).forEach((k) => { - let m = ads(si.members[k]); - if (!m.members && !m.sizeof) { - m.sizeof = sigSize(m.signature); - if (!m.sizeof) toss(sPropName(structName, k), "is missing a sizeof property.", m); - } - if (void 0 === m.offset) if (autoCalc) m.offset = offset; - else toss(sPropName(structName, k), "is missing its offset.", JSON.stringify(m)); - si.members[k] = m; - if (!lastMember || lastMember.offset < m.offset) lastMember = m; - const oldAutoCalc = !!m.autoCalc; - if (autoCalc) m.autoCalcSizeOffset = true; - makeMemberWrapper.call(self, StructCtor, k, m); - if (oldAutoCalc) m.autoCalcSizeOffset = true; - else delete m.autoCalcSizeOffset; - offset += m.sizeof; - }); - if (!lastMember) toss("No member property descriptions found."); - if (!si.sizeof) si.sizeof = offset; - if (si.sizeof === 1) si.signature === "c" || si.signature === "C" || toss("Unexpected sizeof==1 member", sPropName(structName, k), "with signature", si.signature); - else { - if (0 !== si.sizeof % 4) { - console.warn("Invalid struct member description", si); - toss(structName, "sizeof is not aligned. sizeof=" + si.sizeof); - } - if (0 !== si.offset % 4) { - console.warn("Invalid struct member description", si); - toss(structName, "offset is not aligned. offset=" + si.offset); - } - } - if (si.sizeof < offset) { - console.warn("Suspect struct description:", si, "offset =", offset); - toss("Mismatch in the calculated vs. the provided sizeof/offset info.", "Expected sizeof", offset, "but got", si.sizeof, "for", si); - } - delete si.autoCalcSizeOffset; - return StructCtor; - }; - const StructBinder = function StructBinder(structName, structInfo) { - return 1 == arguments.length ? StructBinderImpl.call(StructBinder, structName) : StructBinderImpl.call(StructBinder, structName, structInfo); - }; - StructBinder.StructType = StructType; - StructBinder.config = config; - StructBinder.allocCString = __allocCString; - StructBinder.adaptGet = __adaptGet; - StructBinder.adaptSet = __adaptSet; - StructBinder.adaptStruct = __adaptStruct; - StructBinder.ptrAdd = __ptrAdd; - if (!StructBinder.debugFlags) StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags); - return StructBinder; - }; - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - "use strict"; - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; - globalThis.WhWasmUtilInstaller(wasm); - delete globalThis.WhWasmUtilInstaller; - /** - Signatures for the WASM-exported C-side functions. Each entry - is an array with 2+ elements: - - [ "c-side name", - "result type" (wasm.xWrap() syntax), - [arg types in xWrap() syntax] - // ^^^ this needn't strictly be an array: it can be subsequent - // elements instead: [x,y,z] is equivalent to x,y,z - ] - - Support for the API-specific data types in the result/argument - type strings gets plugged in at a later phase in the API - initialization process. - */ - const bindingSignatures = { - core: [ - [ - "sqlite3_aggregate_context", - "void*", - "sqlite3_context*", - "int" - ], - [ - "sqlite3_bind_double", - "int", - "sqlite3_stmt*", - "int", - "f64" - ], - [ - "sqlite3_bind_int", - "int", - "sqlite3_stmt*", - "int", - "int" - ], - [ - "sqlite3_bind_null", - void 0, - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_bind_parameter_count", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_bind_parameter_index", - "int", - "sqlite3_stmt*", - "string" - ], - [ - "sqlite3_bind_parameter_name", - "string", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_bind_pointer", - "int", - "sqlite3_stmt*", - "int", - "*", - "string:static", - "*" - ], - [ - "sqlite3_busy_handler", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - signature: "i(pi)", - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_busy_timeout", - "int", - "sqlite3*", - "int" - ], - [ - "sqlite3_changes", - "int", - "sqlite3*" - ], - [ - "sqlite3_clear_bindings", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_collation_needed", - "int", - "sqlite3*", - "*", - "*" - ], - [ - "sqlite3_column_blob", - "*", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_bytes", - "int", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_count", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_column_decltype", - "string", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_double", - "f64", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_int", - "int", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_name", - "string", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_type", - "int", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_value", - "sqlite3_value*", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_commit_hook", - "void*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_commit_hook", - signature: "i(p)", - contextKey: (argv) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_compileoption_get", - "string", - "int" - ], - [ - "sqlite3_compileoption_used", - "int", - "string" - ], - [ - "sqlite3_complete", - "int", - "string:flexible" - ], - [ - "sqlite3_context_db_handle", - "sqlite3*", - "sqlite3_context*" - ], - [ - "sqlite3_data_count", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_db_filename", - "string", - "sqlite3*", - "string" - ], - [ - "sqlite3_db_handle", - "sqlite3*", - "sqlite3_stmt*" - ], - [ - "sqlite3_db_name", - "string", - "sqlite3*", - "int" - ], - [ - "sqlite3_db_readonly", - "int", - "sqlite3*", - "string" - ], - [ - "sqlite3_db_status", - "int", - "sqlite3*", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_errcode", - "int", - "sqlite3*" - ], - [ - "sqlite3_errmsg", - "string", - "sqlite3*" - ], - [ - "sqlite3_error_offset", - "int", - "sqlite3*" - ], - [ - "sqlite3_errstr", - "string", - "int" - ], - [ - "sqlite3_exec", - "int", - [ - "sqlite3*", - "string:flexible", - new wasm.xWrap.FuncPtrAdapter({ - signature: "i(pipp)", - bindScope: "transient", - callProxy: (callback) => { - let aNames; - return (pVoid, nCols, pColVals, pColNames) => { - try { - const aVals = wasm.cArgvToJs(nCols, pColVals); - if (!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); - return callback(aVals, aNames) | 0; - } catch (e) { - return e.resultCode || capi.SQLITE_ERROR; - } - }; - } - }), - "*", - "**" - ] - ], - [ - "sqlite3_expanded_sql", - "string", - "sqlite3_stmt*" - ], - [ - "sqlite3_extended_errcode", - "int", - "sqlite3*" - ], - [ - "sqlite3_extended_result_codes", - "int", - "sqlite3*", - "int" - ], - [ - "sqlite3_file_control", - "int", - "sqlite3*", - "string", - "int", - "*" - ], - [ - "sqlite3_finalize", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_free", - void 0, - "*" - ], - [ - "sqlite3_get_autocommit", - "int", - "sqlite3*" - ], - [ - "sqlite3_get_auxdata", - "*", - "sqlite3_context*", - "int" - ], - ["sqlite3_initialize", void 0], - [ - "sqlite3_interrupt", - void 0, - "sqlite3*" - ], - [ - "sqlite3_is_interrupted", - "int", - "sqlite3*" - ], - ["sqlite3_keyword_count", "int"], - [ - "sqlite3_keyword_name", - "int", - [ - "int", - "**", - "*" - ] - ], - [ - "sqlite3_keyword_check", - "int", - ["string", "int"] - ], - ["sqlite3_libversion", "string"], - ["sqlite3_libversion_number", "int"], - [ - "sqlite3_limit", - "int", - [ - "sqlite3*", - "int", - "int" - ] - ], - [ - "sqlite3_malloc", - "*", - "int" - ], - [ - "sqlite3_next_stmt", - "sqlite3_stmt*", - ["sqlite3*", "sqlite3_stmt*"] - ], - [ - "sqlite3_open", - "int", - "string", - "*" - ], - [ - "sqlite3_open_v2", - "int", - "string", - "*", - "int", - "string" - ], - [ - "sqlite3_realloc", - "*", - "*", - "int" - ], - [ - "sqlite3_reset", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_result_blob", - void 0, - "sqlite3_context*", - "*", - "int", - "*" - ], - [ - "sqlite3_result_double", - void 0, - "sqlite3_context*", - "f64" - ], - [ - "sqlite3_result_error", - void 0, - "sqlite3_context*", - "string", - "int" - ], - [ - "sqlite3_result_error_code", - void 0, - "sqlite3_context*", - "int" - ], - [ - "sqlite3_result_error_nomem", - void 0, - "sqlite3_context*" - ], - [ - "sqlite3_result_error_toobig", - void 0, - "sqlite3_context*" - ], - [ - "sqlite3_result_int", - void 0, - "sqlite3_context*", - "int" - ], - [ - "sqlite3_result_null", - void 0, - "sqlite3_context*" - ], - [ - "sqlite3_result_pointer", - void 0, - "sqlite3_context*", - "*", - "string:static", - "*" - ], - [ - "sqlite3_result_subtype", - void 0, - "sqlite3_value*", - "int" - ], - [ - "sqlite3_result_text", - void 0, - "sqlite3_context*", - "string", - "int", - "*" - ], - [ - "sqlite3_result_zeroblob", - void 0, - "sqlite3_context*", - "int" - ], - [ - "sqlite3_rollback_hook", - "void*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_rollback_hook", - signature: "v(p)", - contextKey: (argv) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_set_auxdata", - void 0, - [ - "sqlite3_context*", - "int", - "*", - "*" - ] - ], - [ - "sqlite3_set_errmsg", - "int", - "sqlite3*", - "int", - "string" - ], - ["sqlite3_shutdown", void 0], - ["sqlite3_sourceid", "string"], - [ - "sqlite3_sql", - "string", - "sqlite3_stmt*" - ], - [ - "sqlite3_status", - "int", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_step", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_stmt_busy", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_stmt_readonly", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_stmt_status", - "int", - "sqlite3_stmt*", - "int", - "int" - ], - [ - "sqlite3_strglob", - "int", - "string", - "string" - ], - [ - "sqlite3_stricmp", - "int", - "string", - "string" - ], - [ - "sqlite3_strlike", - "int", - "string", - "string", - "int" - ], - [ - "sqlite3_strnicmp", - "int", - "string", - "string", - "int" - ], - [ - "sqlite3_table_column_metadata", - "int", - "sqlite3*", - "string", - "string", - "string", - "**", - "**", - "*", - "*", - "*" - ], - [ - "sqlite3_total_changes", - "int", - "sqlite3*" - ], - [ - "sqlite3_trace_v2", - "int", - [ - "sqlite3*", - "int", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_trace_v2::callback", - signature: "i(ippp)", - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_txn_state", - "int", - ["sqlite3*", "string"] - ], - [ - "sqlite3_uri_boolean", - "int", - "sqlite3_filename", - "string", - "int" - ], - [ - "sqlite3_uri_key", - "string", - "sqlite3_filename", - "int" - ], - [ - "sqlite3_uri_parameter", - "string", - "sqlite3_filename", - "string" - ], - [ - "sqlite3_user_data", - "void*", - "sqlite3_context*" - ], - [ - "sqlite3_value_blob", - "*", - "sqlite3_value*" - ], - [ - "sqlite3_value_bytes", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_double", - "f64", - "sqlite3_value*" - ], - [ - "sqlite3_value_dup", - "sqlite3_value*", - "sqlite3_value*" - ], - [ - "sqlite3_value_free", - void 0, - "sqlite3_value*" - ], - [ - "sqlite3_value_frombind", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_int", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_nochange", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_numeric_type", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_pointer", - "*", - "sqlite3_value*", - "string:static" - ], - [ - "sqlite3_value_subtype", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_type", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_vfs_find", - "*", - "string" - ], - [ - "sqlite3_vfs_register", - "int", - "sqlite3_vfs*", - "int" - ], - [ - "sqlite3_vfs_unregister", - "int", - "sqlite3_vfs*" - ] - ], - int64: [ - [ - "sqlite3_bind_int64", - "int", - [ - "sqlite3_stmt*", - "int", - "i64" - ] - ], - [ - "sqlite3_changes64", - "i64", - ["sqlite3*"] - ], - [ - "sqlite3_column_int64", - "i64", - ["sqlite3_stmt*", "int"] - ], - [ - "sqlite3_deserialize", - "int", - "sqlite3*", - "string", - "*", - "i64", - "i64", - "int" - ], - [ - "sqlite3_last_insert_rowid", - "i64", - ["sqlite3*"] - ], - [ - "sqlite3_malloc64", - "*", - "i64" - ], - [ - "sqlite3_msize", - "i64", - "*" - ], - [ - "sqlite3_overload_function", - "int", - [ - "sqlite3*", - "string", - "int" - ] - ], - [ - "sqlite3_realloc64", - "*", - "*", - "i64" - ], - [ - "sqlite3_result_int64", - void 0, - "*", - "i64" - ], - [ - "sqlite3_result_zeroblob64", - "int", - "*", - "i64" - ], - [ - "sqlite3_serialize", - "*", - "sqlite3*", - "string", - "*", - "int" - ], - [ - "sqlite3_set_last_insert_rowid", - void 0, - ["sqlite3*", "i64"] - ], - [ - "sqlite3_status64", - "int", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_db_status64", - "int", - "sqlite3*", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_total_changes64", - "i64", - ["sqlite3*"] - ], - [ - "sqlite3_update_hook", - "*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_update_hook::callback", - signature: "v(pippj)", - contextKey: (argv) => argv[0], - callProxy: (callback) => { - return (p, op, z0, z1, rowid) => { - callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid); - }; - } - }), - "*" - ] - ], - [ - "sqlite3_uri_int64", - "i64", - [ - "sqlite3_filename", - "string", - "i64" - ] - ], - [ - "sqlite3_value_int64", - "i64", - "sqlite3_value*" - ] - ], - wasmInternal: [ - [ - "sqlite3__wasm_db_reset", - "int", - "sqlite3*" - ], - [ - "sqlite3__wasm_db_vfs", - "sqlite3_vfs*", - "sqlite3*", - "string" - ], - [ - "sqlite3__wasm_vfs_create_file", - "int", - "sqlite3_vfs*", - "string", - "*", - "int" - ], - [ - "sqlite3__wasm_posix_create_file", - "int", - "string", - "*", - "int" - ], - [ - "sqlite3__wasm_vfs_unlink", - "int", - "sqlite3_vfs*", - "string" - ], - [ - "sqlite3__wasm_qfmt_token", - "string:dealloc", - "string", - "int" - ] - ] - }; - if (!!wasm.exports.sqlite3_progress_handler) bindingSignatures.core.push([ - "sqlite3_progress_handler", - void 0, - [ - "sqlite3*", - "int", - new wasm.xWrap.FuncPtrAdapter({ - name: "xProgressHandler", - signature: "i(p)", - bindScope: "context", - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ]); - if (!!wasm.exports.sqlite3_stmt_explain) bindingSignatures.core.push([ - "sqlite3_stmt_explain", - "int", - "sqlite3_stmt*", - "int" - ], [ - "sqlite3_stmt_isexplain", - "int", - "sqlite3_stmt*" - ]); - if (!!wasm.exports.sqlite3_set_authorizer) bindingSignatures.core.push([ - "sqlite3_set_authorizer", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_set_authorizer::xAuth", - signature: "i(pissss)", - contextKey: (argv, argIndex) => argv[0], - callProxy: (callback) => { - return (pV, iCode, s0, s1, s2, s3) => { - try { - s0 = s0 && wasm.cstrToJs(s0); - s1 = s1 && wasm.cstrToJs(s1); - s2 = s2 && wasm.cstrToJs(s2); - s3 = s3 && wasm.cstrToJs(s3); - return callback(pV, iCode, s0, s1, s2, s3) | 0; - } catch (e) { - return e.resultCode || capi.SQLITE_ERROR; - } - }; - } - }), - "*" - ] - ]); - if (!!wasm.exports.sqlite3_column_origin_name) bindingSignatures.core.push([ - "sqlite3_column_database_name", - "string", - "sqlite3_stmt*", - "int" - ], [ - "sqlite3_column_origin_name", - "string", - "sqlite3_stmt*", - "int" - ], [ - "sqlite3_column_table_name", - "string", - "sqlite3_stmt*", - "int" - ]); - if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_declare_vtab) bindingSignatures.int64.push([ - "sqlite3_create_module", - "int", - [ - "sqlite3*", - "string", - "sqlite3_module*", - "*" - ] - ], [ - "sqlite3_create_module_v2", - "int", - [ - "sqlite3*", - "string", - "sqlite3_module*", - "*", - "*" - ] - ], [ - "sqlite3_declare_vtab", - "int", - ["sqlite3*", "string:flexible"] - ], [ - "sqlite3_drop_modules", - "int", - ["sqlite3*", "**"] - ], [ - "sqlite3_vtab_collation", - "string", - "sqlite3_index_info*", - "int" - ], [ - "sqlite3_vtab_distinct", - "int", - "sqlite3_index_info*" - ], [ - "sqlite3_vtab_in", - "int", - "sqlite3_index_info*", - "int", - "int" - ], [ - "sqlite3_vtab_in_first", - "int", - "sqlite3_value*", - "**" - ], [ - "sqlite3_vtab_in_next", - "int", - "sqlite3_value*", - "**" - ], [ - "sqlite3_vtab_nochange", - "int", - "sqlite3_context*" - ], [ - "sqlite3_vtab_on_conflict", - "int", - "sqlite3*" - ], [ - "sqlite3_vtab_rhs_value", - "int", - "sqlite3_index_info*", - "int", - "**" - ]); - if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_preupdate_hook) bindingSignatures.int64.push([ - "sqlite3_preupdate_blobwrite", - "int", - "sqlite3*" - ], [ - "sqlite3_preupdate_count", - "int", - "sqlite3*" - ], [ - "sqlite3_preupdate_depth", - "int", - "sqlite3*" - ], [ - "sqlite3_preupdate_hook", - "*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_preupdate_hook", - signature: "v(ppippjj)", - contextKey: (argv) => argv[0], - callProxy: (callback) => { - return (p, db, op, zDb, zTbl, iKey1, iKey2) => { - callback(p, db, op, wasm.cstrToJs(zDb), wasm.cstrToJs(zTbl), iKey1, iKey2); - }; - } - }), - "*" - ] - ], [ - "sqlite3_preupdate_new", - "int", - [ - "sqlite3*", - "int", - "**" - ] - ], [ - "sqlite3_preupdate_old", - "int", - [ - "sqlite3*", - "int", - "**" - ] - ]); - if (wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add && !!wasm.exports.sqlite3session_create && !!wasm.exports.sqlite3_preupdate_hook) { - /** - FuncPtrAdapter options for session-related callbacks with the - native signature "i(ps)". This proxy converts the 2nd argument - from a C string to a JS string before passing the arguments on - to the client-provided JS callback. - */ - const __ipsProxy = { - signature: "i(ps)", - callProxy: (callback) => { - return (p, s) => { - try { - return callback(p, wasm.cstrToJs(s)) | 0; - } catch (e) { - return e.resultCode || capi.SQLITE_ERROR; - } - }; - } - }; - bindingSignatures.int64.push([ - "sqlite3changegroup_add", - "int", - [ - "sqlite3_changegroup*", - "int", - "void*" - ] - ], [ - "sqlite3changegroup_add_strm", - "int", - [ - "sqlite3_changegroup*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changegroup_delete", - void 0, - ["sqlite3_changegroup*"] - ], [ - "sqlite3changegroup_new", - "int", - ["**"] - ], [ - "sqlite3changegroup_output", - "int", - [ - "sqlite3_changegroup*", - "int*", - "**" - ] - ], [ - "sqlite3changegroup_output_strm", - "int", - [ - "sqlite3_changegroup*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppi)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_apply", - "int", - [ - "sqlite3*", - "int", - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_apply_strm", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_apply_v2", - "int", - [ - "sqlite3*", - "int", - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_apply_v2_strm", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_apply_v3", - "int", - [ - "sqlite3*", - "int", - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - signature: "i(pp)", - bindScope: "transient" - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_apply_v3_strm", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - signature: "i(pp)", - bindScope: "transient" - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_concat", - "int", - [ - "int", - "void*", - "int", - "void*", - "int*", - "**" - ] - ], [ - "sqlite3changeset_concat_strm", - "int", - [ - new wasm.xWrap.FuncPtrAdapter({ - name: "xInputA", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInputB", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppi)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_conflict", - "int", - [ - "sqlite3_changeset_iter*", - "int", - "**" - ] - ], [ - "sqlite3changeset_finalize", - "int", - ["sqlite3_changeset_iter*"] - ], [ - "sqlite3changeset_fk_conflicts", - "int", - ["sqlite3_changeset_iter*", "int*"] - ], [ - "sqlite3changeset_invert", - "int", - [ - "int", - "void*", - "int*", - "**" - ] - ], [ - "sqlite3changeset_invert_strm", - "int", - [ - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppi)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_new", - "int", - [ - "sqlite3_changeset_iter*", - "int", - "**" - ] - ], [ - "sqlite3changeset_next", - "int", - ["sqlite3_changeset_iter*"] - ], [ - "sqlite3changeset_old", - "int", - [ - "sqlite3_changeset_iter*", - "int", - "**" - ] - ], [ - "sqlite3changeset_op", - "int", - [ - "sqlite3_changeset_iter*", - "**", - "int*", - "int*", - "int*" - ] - ], [ - "sqlite3changeset_pk", - "int", - [ - "sqlite3_changeset_iter*", - "**", - "int*" - ] - ], [ - "sqlite3changeset_start", - "int", - [ - "**", - "int", - "*" - ] - ], [ - "sqlite3changeset_start_strm", - "int", - [ - "**", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_start_v2", - "int", - [ - "**", - "int", - "*", - "int" - ] - ], [ - "sqlite3changeset_start_v2_strm", - "int", - [ - "**", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - "int" - ] - ], [ - "sqlite3session_attach", - "int", - ["sqlite3_session*", "string"] - ], [ - "sqlite3session_changeset", - "int", - [ - "sqlite3_session*", - "int*", - "**" - ] - ], [ - "sqlite3session_changeset_size", - "i64", - ["sqlite3_session*"] - ], [ - "sqlite3session_changeset_strm", - "int", - [ - "sqlite3_session*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3session_config", - "int", - ["int", "void*"] - ], [ - "sqlite3session_create", - "int", - [ - "sqlite3*", - "string", - "**" - ] - ], [ - "sqlite3session_diff", - "int", - [ - "sqlite3_session*", - "string", - "string", - "**" - ] - ], [ - "sqlite3session_enable", - "int", - ["sqlite3_session*", "int"] - ], [ - "sqlite3session_indirect", - "int", - ["sqlite3_session*", "int"] - ], [ - "sqlite3session_isempty", - "int", - ["sqlite3_session*"] - ], [ - "sqlite3session_memory_used", - "i64", - ["sqlite3_session*"] - ], [ - "sqlite3session_object_config", - "int", - [ - "sqlite3_session*", - "int", - "void*" - ] - ], [ - "sqlite3session_patchset", - "int", - [ - "sqlite3_session*", - "*", - "**" - ] - ], [ - "sqlite3session_patchset_strm", - "int", - [ - "sqlite3_session*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3session_table_filter", - void 0, - [ - "sqlite3_session*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - ...__ipsProxy, - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ]); - } - /** - Prepare JS<->C struct bindings for the non-opaque struct types we - need... - */ - sqlite3.StructBinder = globalThis.Jaccwabyt({ - heap: wasm.heap8u, - alloc: wasm.alloc, - dealloc: wasm.dealloc, - bigIntEnabled: wasm.bigIntEnabled, - pointerIR: wasm.ptr.ir, - memberPrefix: "$" - }); - delete globalThis.Jaccwabyt; - { - const __xString = wasm.xWrap.argAdapter("string"); - wasm.xWrap.argAdapter("string:flexible", (v) => __xString(util.flexibleString(v))); - /** - The 'string:static' argument adapter treats its argument as - either... - - - WASM pointer: assumed to be a long-lived C-string which gets - returned as-is. - - - Anything else: gets coerced to a JS string for use as a map - key. If a matching entry is found (as described next), it is - returned, else wasm.allocCString() is used to create a a new - string, map its pointer to a copy of (''+v) for the remainder - of the application's life, and returns that pointer value for - this call and all future calls which are passed a - string-equivalent argument. - - Use case: sqlite3_bind_pointer(), sqlite3_result_pointer(), and - sqlite3_value_pointer() call for "a static string and - preferably a string literal". This converter is used to ensure - that the string value seen by those functions is long-lived and - behaves as they need it to, at the cost of a one-time leak of - each distinct key. - */ - wasm.xWrap.argAdapter("string:static", function(v) { - if (wasm.isPtr(v)) return v; - v = "" + v; - return this[v] || (this[v] = wasm.allocCString(v)); - }.bind(Object.create(null))); - /** - Add some descriptive xWrap() aliases for '*' intended to (A) - improve readability/correctness of bindingSignatures and (B) - provide automatic conversion from higher-level representations, - e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via (capi.sqlite3_vfs - instance).pointer. - */ - const __xArgPtr = wasm.xWrap.argAdapter("*"); - const nilType = function() {}; - wasm.xWrap.argAdapter("sqlite3_filename", __xArgPtr)("sqlite3_context*", __xArgPtr)("sqlite3_value*", __xArgPtr)("void*", __xArgPtr)("sqlite3_changegroup*", __xArgPtr)("sqlite3_changeset_iter*", __xArgPtr)("sqlite3_session*", __xArgPtr)("sqlite3_stmt*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.Stmt || nilType) ? v.pointer : v))("sqlite3*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.DB || nilType) ? v.pointer : v))("sqlite3_vfs*", (v) => { - if ("string" === typeof v) return capi.sqlite3_vfs_find(v) || sqlite3.SQLite3Error.toss(capi.SQLITE_NOTFOUND, "Unknown sqlite3_vfs name:", v); - return __xArgPtr(v instanceof (capi.sqlite3_vfs || nilType) ? v.pointer : v); - }); - if (wasm.exports.sqlite3_declare_vtab) wasm.xWrap.argAdapter("sqlite3_index_info*", (v) => __xArgPtr(v instanceof (capi.sqlite3_index_info || nilType) ? v.pointer : v))("sqlite3_module*", (v) => __xArgPtr(v instanceof (capi.sqlite3_module || nilType) ? v.pointer : v)); - /** - Alias `T*` to `*` for return type conversions for common T - types, primarily to improve legibility of their binding - signatures. - */ - const __xRcPtr = wasm.xWrap.resultAdapter("*"); - wasm.xWrap.resultAdapter("sqlite3*", __xRcPtr)("sqlite3_context*", __xRcPtr)("sqlite3_stmt*", __xRcPtr)("sqlite3_value*", __xRcPtr)("sqlite3_vfs*", __xRcPtr)("void*", __xRcPtr); - /** - Populate api object with sqlite3_...() by binding the "raw" wasm - exports into type-converting proxies using wasm.xWrap(). - */ - for (const e of bindingSignatures.core) capi[e[0]] = wasm.xWrap.apply(null, e); - for (const e of bindingSignatures.wasmInternal) util[e[0]] = wasm.xWrap.apply(null, e); - for (const e of bindingSignatures.int64) capi[e[0]] = wasm.bigIntEnabled ? wasm.xWrap.apply(null, e) : () => toss(e[0] + "() is unavailable due to lack", "of BigInt support in this build."); - delete bindingSignatures.core; - delete bindingSignatures.int64; - delete bindingSignatures.wasmInternal; - /** - Sets the given db's error state. Accepts: - - - (sqlite3*, int code, string msg) - - (sqlite3*, Error e [,string msg = ''+e]) - - If passed a WasmAllocError, the message is ignored and the - result code is SQLITE_NOMEM. If passed any other Error type, - the result code defaults to SQLITE_ERROR unless the Error - object has a resultCode property, in which case that is used - (e.g. SQLite3Error has that). If passed a non-WasmAllocError - exception, the message string defaults to ''+theError. - - Returns either the final result code, capi.SQLITE_NOMEM if - setting the message string triggers an OOM, or - capi.SQLITE_MISUSE if pDb is NULL or invalid (with the caveat - that behavior in the later case is undefined if pDb is not - "valid enough"). - - Pass (pDb,0,0) to clear the error state. - */ - util.sqlite3__wasm_db_error = function(pDb, resultCode, message) { - if (!pDb) return capi.SQLITE_MISUSE; - if (resultCode instanceof sqlite3.WasmAllocError) { - resultCode = capi.SQLITE_NOMEM; - message = 0; - } else if (resultCode instanceof Error) { - message = message || "" + resultCode; - resultCode = resultCode.resultCode || capi.SQLITE_ERROR; - } - return capi.sqlite3_set_errmsg(pDb, resultCode, message) || resultCode; - }; - } - { - const cJson = wasm.xCall("sqlite3__wasm_enum_json"); - if (!cJson) toss("Maintenance required: increase sqlite3__wasm_enum_json()'s", "static buffer size!"); - wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); - const defineGroups = [ - "access", - "authorizer", - "blobFinalizers", - "changeset", - "config", - "dataTypes", - "dbConfig", - "dbStatus", - "encodings", - "fcntl", - "flock", - "ioCap", - "limits", - "openFlags", - "prepareFlags", - "resultCodes", - "sqlite3Status", - "stmtStatus", - "syncFlags", - "trace", - "txnState", - "udfFlags", - "version" - ]; - if (wasm.bigIntEnabled) defineGroups.push("serialize", "session", "vtab"); - for (const t of defineGroups) for (const e of Object.entries(wasm.ctype[t])) capi[e[0]] = e[1]; - if (!wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)) toss("Internal error: cannot resolve exported function", "entry SQLITE_WASM_DEALLOC (==" + capi.SQLITE_WASM_DEALLOC + ")."); - const __rcMap = Object.create(null); - for (const e of Object.entries(wasm.ctype["resultCodes"])) __rcMap[e[1]] = e[0]; - /** - For the given integer, returns the SQLITE_xxx result code as a - string, or undefined if no such mapping is found. - */ - capi.sqlite3_js_rc_str = (rc) => __rcMap[rc]; - const notThese = Object.assign(Object.create(null), { - WasmTestStruct: true, - sqlite3_index_info: !wasm.bigIntEnabled, - sqlite3_index_constraint: !wasm.bigIntEnabled, - sqlite3_index_orderby: !wasm.bigIntEnabled, - sqlite3_index_constraint_usage: !wasm.bigIntEnabled - }); - for (const s of wasm.ctype.structs) if (!notThese[s.name]) capi[s.name] = sqlite3.StructBinder(s); - if (capi.sqlite3_index_info) { - for (const k of [ - "sqlite3_index_constraint", - "sqlite3_index_orderby", - "sqlite3_index_constraint_usage" - ]) { - capi.sqlite3_index_info[k] = capi[k]; - delete capi[k]; - } - capi.sqlite3_vtab_config = wasm.xWrap("sqlite3__wasm_vtab_config", "int", [ - "sqlite3*", - "int", - "int" - ]); - } - } - /** - Internal helper to assist in validating call argument counts in - the hand-written sqlite3_xyz() wrappers. We do this only for - consistency with non-special-case wrappings. - */ - const __dbArgcMismatch = (pDb, f, n) => { - return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, f + "() requires " + n + " argument" + (1 === n ? "" : "s") + "."); - }; - /** Code duplication reducer for functions which take an encoding - argument and require SQLITE_UTF8. Sets the db error code to - SQLITE_FORMAT, installs a descriptive error message, - and returns SQLITE_FORMAT. */ - const __errEncoding = (pDb) => { - return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding."); - }; - /** - __dbCleanupMap is infrastructure for recording registration of - UDFs and collations so that sqlite3_close_v2() can clean up any - automated JS-to-WASM function conversions installed by those. - */ - const __argPDb = (pDb) => wasm.xWrap.argAdapter("sqlite3*")(pDb); - const __argStr = (str) => wasm.isPtr(str) ? wasm.cstrToJs(str) : str; - const __dbCleanupMap = function(pDb, mode) { - pDb = __argPDb(pDb); - let m = this.dbMap.get(pDb); - if (!mode) { - this.dbMap.delete(pDb); - return m; - } else if (!m && mode > 0) this.dbMap.set(pDb, m = Object.create(null)); - return m; - }.bind(Object.assign(Object.create(null), { dbMap: /* @__PURE__ */ new Map() })); - __dbCleanupMap.addCollation = function(pDb, name) { - const m = __dbCleanupMap(pDb, 1); - if (!m.collation) m.collation = /* @__PURE__ */ new Set(); - m.collation.add(__argStr(name).toLowerCase()); - }; - __dbCleanupMap._addUDF = function(pDb, name, arity, map) { - name = __argStr(name).toLowerCase(); - let u = map.get(name); - if (!u) map.set(name, u = /* @__PURE__ */ new Set()); - u.add(arity < 0 ? -1 : arity); - }; - __dbCleanupMap.addFunction = function(pDb, name, arity) { - const m = __dbCleanupMap(pDb, 1); - if (!m.udf) m.udf = /* @__PURE__ */ new Map(); - this._addUDF(pDb, name, arity, m.udf); - }; - if (wasm.exports.sqlite3_create_window_function) __dbCleanupMap.addWindowFunc = function(pDb, name, arity) { - const m = __dbCleanupMap(pDb, 1); - if (!m.wudf) m.wudf = /* @__PURE__ */ new Map(); - this._addUDF(pDb, name, arity, m.wudf); - }; - /** - Intended to be called _only_ from sqlite3_close_v2(), - passed its non-0 db argument. - - This function frees up certain automatically-installed WASM - function bindings which were installed on behalf of the given db, - as those may otherwise leak. - - Notable caveat: this is only ever run via - sqlite3.capi.sqlite3_close_v2(). If a client, for whatever - reason, uses sqlite3.wasm.exports.sqlite3_close_v2() (the - function directly exported from WASM), this cleanup will not - happen. - - This is not a silver bullet for avoiding automation-related - leaks but represents "an honest effort." - - The issue being addressed here is covered at: - - https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr - */ - __dbCleanupMap.cleanup = function(pDb) { - pDb = __argPDb(pDb); - /** - Installing NULL functions in the C API will remove those - bindings. The FuncPtrAdapter which sits between us and the C - API will also treat that as an opportunity to - wasm.uninstallFunction() any WASM function bindings it has - installed for pDb. - */ - for (const obj of [ - ["sqlite3_busy_handler", 3], - ["sqlite3_commit_hook", 3], - ["sqlite3_preupdate_hook", 3], - ["sqlite3_progress_handler", 4], - ["sqlite3_rollback_hook", 3], - ["sqlite3_set_authorizer", 3], - ["sqlite3_trace_v2", 4], - ["sqlite3_update_hook", 3] - ]) { - const [name, arity] = obj; - if (!wasm.exports[name]) continue; - const closeArgs = [pDb]; - closeArgs.length = arity; - try { - capi[name](...closeArgs); - } catch (e) { - sqlite3.config.warn("close-time call of", name + "(", closeArgs, ") threw:", e); - } - } - const m = __dbCleanupMap(pDb, 0); - if (!m) return; - if (m.collation) { - for (const name of m.collation) try { - capi.sqlite3_create_collation_v2(pDb, name, capi.SQLITE_UTF8, 0, 0, 0); - } catch (e) {} - delete m.collation; - } - let i; - for (i = 0; i < 2; ++i) { - const fmap = i ? m.wudf : m.udf; - if (!fmap) continue; - const func = i ? capi.sqlite3_create_window_function : capi.sqlite3_create_function_v2; - for (const e of fmap) { - const name = e[0], arities = e[1]; - const fargs = [ - pDb, - name, - 0, - capi.SQLITE_UTF8, - 0, - 0, - 0, - 0, - 0 - ]; - if (i) fargs.push(0); - for (const arity of arities) try { - fargs[2] = arity; - func.apply(null, fargs); - } catch (e) {} - arities.clear(); - } - fmap.clear(); - } - delete m.udf; - delete m.wudf; - }; - { - const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*"); - capi.sqlite3_close_v2 = function(pDb) { - if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_close_v2", 1); - if (pDb) try { - __dbCleanupMap.cleanup(pDb); - } catch (e) {} - return __sqlite3CloseV2(pDb); - }; - } - if (capi.sqlite3session_create) { - const __sqlite3SessionDelete = wasm.xWrap("sqlite3session_delete", void 0, ["sqlite3_session*"]); - capi.sqlite3session_delete = function(pSession) { - if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3session_delete", 1); - else if (pSession) capi.sqlite3session_table_filter(pSession, 0, 0); - __sqlite3SessionDelete(pSession); - }; - } - { - const contextKey = (argv, argIndex) => { - return "argv[" + argIndex + "]:" + argv[0] + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); - }; - const __sqlite3CreateCollationV2 = wasm.xWrap("sqlite3_create_collation_v2", "int", [ - "sqlite3*", - "string", - "int", - "*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xCompare", - signature: "i(pipip)", - contextKey - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xDestroy", - signature: "v(p)", - contextKey - }) - ]); - /** - Works exactly like C's sqlite3_create_collation_v2() except that: - - 1) It returns capi.SQLITE_FORMAT if the 3rd argument contains - any encoding-related value other than capi.SQLITE_UTF8. No - other encodings are supported. As a special case, if the - bottom 4 bits of that argument are 0, SQLITE_UTF8 is - assumed. - - 2) It accepts JS functions for its function-pointer arguments, - for which it will install WASM-bound proxies. The bindings - are "permanent," in that they will stay in the WASM - environment until it shuts down unless the client calls this - again with the same collation name and a value of 0 or null - for the the function pointer(s). sqlite3_close_v2() will - also clean up such automatically-installed WASM functions. - - For consistency with the C API, it requires the same number of - arguments. It returns capi.SQLITE_MISUSE if passed any other - argument count. - - Returns 0 on success, non-0 on error, in which case the error - state of pDb (of type `sqlite3*` or argument-convertible to it) - may contain more information. - */ - capi.sqlite3_create_collation_v2 = function(pDb, zName, eTextRep, pArg, xCompare, xDestroy) { - if (6 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_collation_v2", 6); - else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; - else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); - try { - const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); - if (0 === rc && xCompare instanceof Function) __dbCleanupMap.addCollation(pDb, zName); - return rc; - } catch (e) { - return util.sqlite3__wasm_db_error(pDb, e); - } - }; - capi.sqlite3_create_collation = (pDb, zName, eTextRep, pArg, xCompare) => { - return 5 === arguments.length ? capi.sqlite3_create_collation_v2(pDb, zName, eTextRep, pArg, xCompare, 0) : __dbArgcMismatch(pDb, "sqlite3_create_collation", 5); - }; - } - { - /** FuncPtrAdapter for contextKey() for sqlite3_create_function() - and friends. */ - const contextKey = function(argv, argIndex) { - return argv[0] + ":" + (argv[2] < 0 ? -1 : argv[2]) + ":" + argIndex + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); - }; - /** - JS proxies for the various sqlite3_create[_window]_function() - callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter. - */ - const __cfProxy = Object.assign(Object.create(null), { - xInverseAndStep: { - signature: "v(pip)", - contextKey, - callProxy: (callback) => { - return (pCtx, argc, pArgv) => { - try { - callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)); - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xFinalAndValue: { - signature: "v(p)", - contextKey, - callProxy: (callback) => { - return (pCtx) => { - try { - capi.sqlite3_result_js(pCtx, callback(pCtx)); - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xFunc: { - signature: "v(pip)", - contextKey, - callProxy: (callback) => { - return (pCtx, argc, pArgv) => { - try { - capi.sqlite3_result_js(pCtx, callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))); - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xDestroy: { - signature: "v(p)", - contextKey, - callProxy: (callback) => { - return (pVoid) => { - try { - callback(pVoid); - } catch (e) { - console.error("UDF xDestroy method threw:", e); - } - }; - } - } - }); - const __sqlite3CreateFunction = wasm.xWrap("sqlite3_create_function_v2", "int", [ - "sqlite3*", - "string", - "int", - "int", - "*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFunc", - ...__cfProxy.xFunc - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xStep", - ...__cfProxy.xInverseAndStep - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xFinal", - ...__cfProxy.xFinalAndValue - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xDestroy", - ...__cfProxy.xDestroy - }) - ]); - const __sqlite3CreateWindowFunction = wasm.exports.sqlite3_create_window_function ? wasm.xWrap("sqlite3_create_window_function", "int", [ - "sqlite3*", - "string", - "int", - "int", - "*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xStep", - ...__cfProxy.xInverseAndStep - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xFinal", - ...__cfProxy.xFinalAndValue - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xValue", - ...__cfProxy.xFinalAndValue - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xInverse", - ...__cfProxy.xInverseAndStep - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xDestroy", - ...__cfProxy.xDestroy - }) - ]) : void 0; - capi.sqlite3_create_function_v2 = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_function_v2", f.length); - else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; - else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); - try { - const rc = __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy); - if (0 === rc && (xFunc instanceof Function || xStep instanceof Function || xFinal instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addFunction(pDb, funcName, nArg); - return rc; - } catch (e) { - console.error("sqlite3_create_function_v2() setup threw:", e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); - } - }; - capi.sqlite3_create_function = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) { - return f.length === arguments.length ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, 0) : __dbArgcMismatch(pDb, "sqlite3_create_function", f.length); - }; - if (__sqlite3CreateWindowFunction) capi.sqlite3_create_window_function = function f(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_window_function", f.length); - else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; - else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); - try { - const rc = __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy); - if (0 === rc && (xStep instanceof Function || xFinal instanceof Function || xValue instanceof Function || xInverse instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); - return rc; - } catch (e) { - console.error("sqlite3_create_window_function() setup threw:", e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); - } - }; - else delete capi.sqlite3_create_window_function; - /** - A _deprecated_ alias for capi.sqlite3_result_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfSetResult = capi.sqlite3_create_function.udfSetResult = capi.sqlite3_result_js; - if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js; - /** - A _deprecated_ alias for capi.sqlite3_values_to_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_values_to_js; - if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js; - /** - A _deprecated_ alias for capi.sqlite3_result_error_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfSetError = capi.sqlite3_create_function.udfSetError = capi.sqlite3_result_error_js; - if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js; - } - { - /** - Helper for string:flexible conversions which requires a - byte-length counterpart argument. Passed a value and its - ostensible length, this function returns [V,N], where V is - either v or a to-string transformed copy of v and N is either n - (if v is a WASM pointer, in which case n might be a BigInt), -1 - (if v is a string or Array), or the byte length of v (if it's a - byte array or ArrayBuffer). - */ - const __flexiString = (v, n) => { - if ("string" === typeof v) n = -1; - else if (util.isSQLableTypedArray(v)) { - n = v.byteLength; - v = wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v); - } else if (Array.isArray(v)) { - v = v.join(""); - n = -1; - } - return [v, n]; - }; - /** - Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). - */ - const __prepare = { - basic: wasm.xWrap("sqlite3_prepare_v3", "int", [ - "sqlite3*", - "string", - "int", - "int", - "**", - "**" - ]), - full: wasm.xWrap("sqlite3_prepare_v3", "int", [ - "sqlite3*", - "*", - "int", - "int", - "**", - "**" - ]) - }; - capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail) { - if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_prepare_v3", f.length); - const [xSql, xSqlLen] = __flexiString(sql, Number(sqlLen)); - switch (typeof xSql) { - case "string": return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); - case typeof wasm.ptr.null: return __prepare.full(pDb, wasm.ptr.coerce(xSql), xSqlLen, prepFlags, ppStmt, pzTail); - default: return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3(). typeof=" + typeof xSql); - } - }; - capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail) { - return f.length === arguments.length ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) : __dbArgcMismatch(pDb, "sqlite3_prepare_v2", f.length); - }; - } - { - const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [ - "sqlite3_stmt*", - "int", - "string", - "int", - "*" - ]); - const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [ - "sqlite3_stmt*", - "int", - "*", - "int", - "*" - ]); - /** Documented in the capi object's initializer. */ - capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_text", f.length); - else if (wasm.isPtr(text) || null === text) return __bindText(pStmt, iCol, text, nText, xDestroy); - else if (text instanceof ArrayBuffer) text = new Uint8Array(text); - else if (Array.isArray(pMem)) text = pMem.join(""); - let p, n; - try { - if (util.isSQLableTypedArray(text)) { - p = wasm.allocFromTypedArray(text); - n = text.byteLength; - } else if ("string" === typeof text) [p, n] = wasm.allocCString(text); - else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_text()."); - return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); - } catch (e) { - wasm.dealloc(p); - return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); - } - }; - /** Documented in the capi object's initializer. */ - capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_blob", f.length); - else if (wasm.isPtr(pMem) || null === pMem) return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy); - else if (pMem instanceof ArrayBuffer) pMem = new Uint8Array(pMem); - else if (Array.isArray(pMem)) pMem = pMem.join(""); - let p, n; - try { - if (util.isBindableTypedArray(pMem)) { - p = wasm.allocFromTypedArray(pMem); - n = nMem >= 0 ? nMem : pMem.byteLength; - } else if ("string" === typeof pMem) [p, n] = wasm.allocCString(pMem); - else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_blob()."); - return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); - } catch (e) { - wasm.dealloc(p); - return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); - } - }; - } - if (!capi.sqlite3_column_text) { - const argStmt = wasm.xWrap.argAdapter("sqlite3_stmt*"), argInt = wasm.xWrap.argAdapter("int"), argValue = wasm.xWrap.argAdapter("sqlite3_value*"), newStr = (cstr, n) => wasm.typedArrayToString(wasm.heap8u(), Number(cstr), Number(cstr) + n); - capi.sqlite3_column_text = function(stmt, colIndex) { - const a0 = argStmt(stmt), a1 = argInt(colIndex); - const cstr = wasm.exports.sqlite3_column_text(a0, a1); - return cstr ? newStr(cstr, wasm.exports.sqlite3_column_bytes(a0, a1)) : null; - }; - capi.sqlite3_value_text = function(val) { - const a0 = argValue(val); - const cstr = wasm.exports.sqlite3_value_text(a0); - return cstr ? newStr(cstr, wasm.exports.sqlite3_value_bytes(a0)) : null; - }; - } - /** - Wraps a small subset of the C API's sqlite3_config() options. - Unsupported options trigger the return of capi.SQLITE_NOTFOUND. - Passing fewer than 2 arguments triggers return of - capi.SQLITE_MISUSE. - */ - capi.sqlite3_config = function(op, ...args) { - if (arguments.length < 2) return capi.SQLITE_MISUSE; - switch (op) { - case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: - case capi.SQLITE_CONFIG_MEMSTATUS: - case capi.SQLITE_CONFIG_SMALL_MALLOC: - case capi.SQLITE_CONFIG_SORTERREF_SIZE: - case capi.SQLITE_CONFIG_STMTJRNL_SPILL: - case capi.SQLITE_CONFIG_URI: return wasm.exports.sqlite3__wasm_config_i(op, args[0]); - case capi.SQLITE_CONFIG_LOOKASIDE: return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]); - case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: return wasm.exports.sqlite3__wasm_config_j(op, args[0]); - case capi.SQLITE_CONFIG_GETMALLOC: - case capi.SQLITE_CONFIG_GETMUTEX: - case capi.SQLITE_CONFIG_GETPCACHE2: - case capi.SQLITE_CONFIG_GETPCACHE: - case capi.SQLITE_CONFIG_HEAP: - case capi.SQLITE_CONFIG_LOG: - case capi.SQLITE_CONFIG_MALLOC: - case capi.SQLITE_CONFIG_MMAP_SIZE: - case capi.SQLITE_CONFIG_MULTITHREAD: - case capi.SQLITE_CONFIG_MUTEX: - case capi.SQLITE_CONFIG_PAGECACHE: - case capi.SQLITE_CONFIG_PCACHE2: - case capi.SQLITE_CONFIG_PCACHE: - case capi.SQLITE_CONFIG_PCACHE_HDRSZ: - case capi.SQLITE_CONFIG_PMASZ: - case capi.SQLITE_CONFIG_SERIALIZED: - case capi.SQLITE_CONFIG_SINGLETHREAD: - case capi.SQLITE_CONFIG_SQLLOG: - case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: - default: return capi.SQLITE_NOTFOUND; - } - }; - { - const __autoExtFptr = /* @__PURE__ */ new Set(); - capi.sqlite3_auto_extension = function(fPtr) { - if (fPtr instanceof Function) fPtr = wasm.installFunction("i(ppp)", fPtr); - else if (1 !== arguments.length || !wasm.isPtr(fPtr)) return capi.SQLITE_MISUSE; - const rc = wasm.exports.sqlite3_auto_extension(fPtr); - if (fPtr !== arguments[0]) if (0 === rc) __autoExtFptr.add(fPtr); - else wasm.uninstallFunction(fPtr); - return rc; - }; - capi.sqlite3_cancel_auto_extension = function(fPtr) { - if (!fPtr || 1 !== arguments.length || !wasm.isPtr(fPtr)) return 0; - return wasm.exports.sqlite3_cancel_auto_extension(fPtr); - }; - capi.sqlite3_reset_auto_extension = function() { - wasm.exports.sqlite3_reset_auto_extension(); - for (const fp of __autoExtFptr) wasm.uninstallFunction(fp); - __autoExtFptr.clear(); - }; - } - wasm.xWrap.FuncPtrAdapter.warnOnUse = true; - const StructBinder = sqlite3.StructBinder; - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructBinder.StructType - target object. - - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error, e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc. - - As a special case, if the given function is a pointer, then - `wasm.functionEntry()` is used to validate that it is a known - function. If so, it is used as-is with no extra level of proxying - or cleanup, else an exception is thrown. It is legal to pass a - value of 0, indicating a NULL pointer, with the caveat that 0 - _is_ a legal function pointer in WASM but it will not be accepted - as such _here_. (Justification: the function at address zero must - be one which initially came from the WASM module, not a method we - want to bind to a virtual table or VFS.) - - This function returns a proxy for itself which is bound to tgt - and takes 2 args (name,func). That function returns the same - thing as this one, permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - - ACHTUNG: because we cannot generically know how to transform JS - exceptions into result codes, the installed functions do no - automatic catching of exceptions. It is critical, to avoid - undefined behavior in the C layer, that methods mapped via - this function do not throw. The exception, as it were, to that - rule is... - - If applyArgcCheck is true then each JS function (as opposed to - function pointers) gets wrapped in a proxy which asserts that it - is passed the expected number of arguments, throwing if the - argument count does not match expectations. That is only intended - for dev-time usage for sanity checking, and may leave the C - environment in an undefined state. - */ - const installMethod = function callee(tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck) { - if (!(tgt instanceof StructBinder.StructType)) toss("Usage error: target object is-not-a StructType."); - else if (!(func instanceof Function) && !wasm.isPtr(func)) toss("Usage error: expecting a Function or WASM pointer to one."); - if (1 === arguments.length) return (n, f) => callee(tgt, n, f, applyArgcCheck); - if (!callee.argcProxy) { - callee.argcProxy = function(tgt, funcName, func, sig) { - return function(...args) { - if (func.length !== arguments.length) toss("Argument mismatch for", tgt.structInfo.name + "::" + funcName + ": Native signature is:", sig); - return func.apply(this, args); - }; - }; - callee.removeFuncList = function() { - if (this.ondispose.__removeFuncList) { - this.ondispose.__removeFuncList.forEach((v, ndx) => { - if (wasm.isPtr(v)) try { - wasm.uninstallFunction(v); - } catch (e) {} - }); - delete this.ondispose.__removeFuncList; - } - }; - } - const sigN = tgt.memberSignature(name); - if (sigN.length < 2) toss("Member", name, "does not have a function pointer signature:", sigN); - const memKey = tgt.memberKey(name); - const fProxy = applyArgcCheck && !wasm.isPtr(func) ? callee.argcProxy(tgt, memKey, func, sigN) : func; - if (wasm.isPtr(fProxy)) { - if (fProxy && !wasm.functionEntry(fProxy)) toss("Pointer", fProxy, "is not a WASM function table entry."); - tgt[memKey] = fProxy; - } else { - const pFunc = wasm.installFunction(fProxy, sigN); - tgt[memKey] = pFunc; - if (!tgt.ondispose || !tgt.ondispose.__removeFuncList) { - tgt.addOnDispose("ondispose.__removeFuncList handler", callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - } - return (n, f) => callee(tgt, n, f, applyArgcCheck); - }; - installMethod.installMethodArgcCheck = false; - /** - Installs methods into the given StructBinder.StructType-type - instance. Each entry in the given methods object must map to a - known member of the given StructType, else an exception will be - triggered. See installMethod() for more details, including the - semantics of the 3rd argument. - - As an exception to the above, if any two or more methods in the - 2nd argument are the exact same function, installMethod() is - _not_ called for the 2nd and subsequent instances, and instead - those instances get assigned the same method pointer which is - created for the first instance. This optimization is primarily to - accommodate special handling of sqlite3_module::xConnect and - xCreate methods. - - On success, returns its first argument. Throws on error. - */ - const installMethods = function(structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck) { - const seen = /* @__PURE__ */ new Map(); - for (const k of Object.keys(methods)) { - const m = methods[k]; - const prior = seen.get(m); - if (prior) { - const mkey = structInstance.memberKey(k); - structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; - } else { - installMethod(structInstance, k, m, applyArgcCheck); - seen.set(m, k); - } - } - return structInstance; - }; - /** - Equivalent to calling installMethod(this,...arguments) with a - first argument of this object. If called with 1 or 2 arguments - and the first is an object, it's instead equivalent to calling - installMethods(this,...arguments). - */ - StructBinder.StructType.prototype.installMethod = function callee(name, func, applyArgcCheck = installMethod.installMethodArgcCheck) { - return arguments.length < 3 && name && "object" === typeof name ? installMethods(this, ...arguments) : installMethod(this, ...arguments); - }; - /** - Equivalent to calling installMethods() with a first argument - of this object. - */ - StructBinder.StructType.prototype.installMethods = function(methods, applyArgcCheck = installMethod.installMethodArgcCheck) { - return installMethods(this, methods, applyArgcCheck); - }; - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - const toss3 = (...args) => { - throw new sqlite3.SQLite3Error(...args); - }; - const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; - const outWrapper = function(f) { - return (...args) => f("sqlite3.oo1:", ...args); - }; - sqlite3.__isUnderTest ? outWrapper(console.debug.bind(console)) : outWrapper(sqlite3.config.debug); - sqlite3.__isUnderTest ? outWrapper(console.warn.bind(console)) : outWrapper(sqlite3.config.warn); - sqlite3.__isUnderTest ? outWrapper(console.error.bind(console)) : outWrapper(sqlite3.config.error); - /** - In order to keep clients from manipulating, perhaps - inadvertently, the underlying pointer values of DB and Stmt - instances, we'll gate access to them via the `pointer` property - accessor and store their real values in this map. Keys = DB/Stmt - objects, values = pointer values. This also unifies how those are - accessed, for potential use downstream via custom - wasm.xWrap() function signatures which know how to extract - it. - */ - const __ptrMap = /* @__PURE__ */ new WeakMap(); - /** - A Set of oo1.DB or oo1.Stmt objects which are proxies for - (sqlite3*) resp. (sqlite3_stmt*) pointers which themselves are - owned elsewhere. Objects in this Set do not own their underlying - handle and that handle must be guaranteed (by the client) to - outlive the proxy. DB.close()/Stmt.finalize() methods will remove - the object from this Set _instead_ of closing/finalizing the - pointer. These proxies are primarily intended as a way to briefly - wrap an (sqlite3[_stmt]*) object as an oo1.DB/Stmt without taking - over ownership, to take advantage of simplifies usage compared to - the C API while not imposing any change of ownership. - - See DB.wrapHandle() and Stmt.wrapHandle(). - */ - const __doesNotOwnHandle = /* @__PURE__ */ new Set(); - /** - Map of DB instances to objects, each object being a map of Stmt - wasm pointers to Stmt objects. - */ - const __stmtMap = /* @__PURE__ */ new WeakMap(); - /** If object opts has _its own_ property named p then that - property's value is returned, else dflt is returned. */ - const getOwnOption = (opts, p, dflt) => { - const d = Object.getOwnPropertyDescriptor(opts, p); - return d ? d.value : dflt; - }; - const checkSqlite3Rc = function(dbPtr, sqliteResultCode) { - if (sqliteResultCode) { - if (dbPtr instanceof DB) dbPtr = dbPtr.pointer; - toss3(sqliteResultCode, "sqlite3 result code", sqliteResultCode + ":", dbPtr ? capi.sqlite3_errmsg(dbPtr) : capi.sqlite3_errstr(sqliteResultCode)); - } - return arguments[0]; - }; - /** - sqlite3_trace_v2() callback which gets installed by the DB ctor - if its open-flags contain "t". - */ - const __dbTraceToConsole = wasm.installFunction("i(ippp)", function(t, c, p, x) { - if (capi.SQLITE_TRACE_STMT === t) console.log("SQL TRACE #" + ++this.counter, "via sqlite3@" + c + "[" + capi.sqlite3_db_filename(c, null) + "]", wasm.cstrToJs(x)); - }.bind({ counter: 0 })); - /** - A map of sqlite3_vfs pointers to SQL code or a callback function - to run when the DB constructor opens a database with the given - VFS. In the latter case, the call signature is - (theDbObject,sqlite3Namespace) and the callback is expected to - throw on error. - */ - const __vfsPostOpenCallback = Object.create(null); - /** - A proxy for DB class constructors. It must be called with the - being-construct DB object as its "this". See the DB constructor - for the argument docs. This is split into a separate function - in order to enable simple creation of special-case DB constructors, - e.g. JsStorageDb and OpfsDb. - - Expects to be passed a configuration object with the following - properties: - - - `.filename`: the db filename. It may be a special name like ":memory:" - or "". It may also be a URI-style name. - - - `.flags`: as documented in the DB constructor. - - - `.vfs`: as documented in the DB constructor. - - It also accepts those as the first 3 arguments. - - In non-default builds it may accept additional configuration - options. - */ - const dbCtorHelper = function ctor(...args) { - const opt = ctor.normalizeArgs(...args); - let pDb; - if (pDb = opt["sqlite3*"]) { - if (!opt["sqlite3*:takeOwnership"]) __doesNotOwnHandle.add(this); - this.filename = capi.sqlite3_db_filename(pDb, "main"); - } else { - let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags; - if ("string" !== typeof fn && !wasm.isPtr(fn) || "string" !== typeof flagsStr || vfsName && "string" !== typeof vfsName && !wasm.isPtr(vfsName)) { - sqlite3.config.error("Invalid DB ctor args", opt, arguments); - toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt); - } - let oflags = 0; - if (flagsStr.indexOf("c") >= 0) oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; - if (flagsStr.indexOf("w") >= 0) oflags |= capi.SQLITE_OPEN_READWRITE; - if (0 === oflags) oflags |= capi.SQLITE_OPEN_READONLY; - oflags |= capi.SQLITE_OPEN_EXRESCODE; - const stack = wasm.pstack.pointer; - try { - const pPtr = wasm.pstack.allocPtr(); - let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || wasm.ptr.null); - pDb = wasm.peekPtr(pPtr); - checkSqlite3Rc(pDb, rc); - capi.sqlite3_extended_result_codes(pDb, 1); - if (flagsStr.indexOf("t") >= 0) capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, pDb); - } catch (e) { - if (pDb) capi.sqlite3_close_v2(pDb); - throw e; - } finally { - wasm.pstack.restore(stack); - } - this.filename = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn; - } - __ptrMap.set(this, pDb); - __stmtMap.set(this, Object.create(null)); - if (!opt["sqlite3*"]) try { - const postInitSql = __vfsPostOpenCallback[capi.sqlite3_js_db_vfs(pDb) || toss3("Internal error: cannot get VFS for new db handle.")]; - if (postInitSql) - /** - Reminder: if this db is encrypted and the client did _not_ pass - in the key, any init code will fail, causing the ctor to throw. - We don't actually know whether the db is encrypted, so we cannot - sensibly apply any heuristics which skip the init code only for - encrypted databases for which no key has yet been supplied. - */ - if (postInitSql instanceof Function) postInitSql(this, sqlite3); - else checkSqlite3Rc(pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)); - } catch (e) { - this.close(); - throw e; - } - }; - /** - Sets a callback which should be called after a db is opened with - the given sqlite3_vfs pointer. The 2nd argument must be a - function, which gets called with - (theOo1DbObject,sqlite3Namespace) at the end of the DB() - constructor. The function must throw on error, in which case the - db is closed and the exception is propagated. This function is - intended only for use by DB subclasses or sqlite3_vfs - implementations. - - Prior to 2024-07-22, it was legal to pass SQL code as the second - argument, but that can interfere with a client's ability to run - pragmas which must be run before anything else, namely (pragma - locking_mode=exclusive) for use with WAL mode. That capability - had only ever been used as an internal detail of the two OPFS - VFSes, and they no longer use it that way. - */ - dbCtorHelper.setVfsPostOpenCallback = function(pVfs, callback) { - if (!(callback instanceof Function)) toss3("dbCtorHelper.setVfsPostOpenCallback() should not be used with a non-function argument.", arguments); - __vfsPostOpenCallback[pVfs] = callback; - }; - /** - A helper for DB constructors. It accepts either a single - config-style object or up to 3 arguments (filename, dbOpenFlags, - dbVfsName). It returns a new object containing: - - { filename: ..., flags: ..., vfs: ... } - - If passed an object, any additional properties it has are copied - as-is into the new object. - */ - dbCtorHelper.normalizeArgs = function(filename = ":memory:", flags = "c", vfs = null) { - const arg = {}; - if (1 === arguments.length && arguments[0] && "object" === typeof arguments[0]) { - Object.assign(arg, arguments[0]); - if (void 0 === arg.flags) arg.flags = "c"; - if (void 0 === arg.vfs) arg.vfs = null; - if (void 0 === arg.filename) arg.filename = ":memory:"; - } else { - arg.filename = filename; - arg.flags = flags; - arg.vfs = vfs; - } - return arg; - }; - /** - The DB class provides a high-level OO wrapper around an sqlite3 - db handle. - - The given db filename must be resolvable using whatever - filesystem layer (virtual or otherwise) is set up for the default - sqlite3 VFS or a VFS which can resolve it must be specified. - - The special sqlite3 db names ":memory:" and "" (temporary db) - have their normal special meanings here and need not resolve to - real filenames, but "" uses an on-storage temporary database and - requires that the VFS support that. - - The second argument specifies the open/create mode for the - database. It must be string containing a sequence of letters (in - any order, but case sensitive) specifying the mode: - - - "c": create if it does not exist, else fail if it does not - exist. Implies the "w" flag. - - - "w": write. Implies "r": a db cannot be write-only. - - - "r": read-only if neither "w" nor "c" are provided, else it - is ignored. - - - "t": enable tracing of SQL executed on this database handle, - sending it to `console.log()`. To disable it later, call - `sqlite3.capi.sqlite3_trace_v2(thisDb.pointer, 0, 0, 0)`. - - If "w" is not provided, the db is implicitly read-only, noting - that "rc" is meaningless - - Any other letters are currently ignored. The default is - "c". These modes are ignored for the special ":memory:" and "" - names and _may_ be ignored altogether for certain VFSes. - - The final argument is analogous to the final argument of - sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value, - or none at all, to use the default. If passed a value, it must - be the string name of a VFS. - - The constructor optionally (and preferably) takes its arguments - in the form of a single configuration object with the following - properties: - - - `filename`: database file name - - `flags`: open-mode flags - - `vfs`: the VFS fname - - - The `filename` and `vfs` arguments may be either JS strings or - C-strings allocated via WASM. `flags` is required to be a JS - string (because it's specific to this API, which is specific - to JS). - - For purposes of passing a DB instance to C-style sqlite3 - functions, the DB object's read-only `pointer` property holds its - `sqlite3*` pointer value. That property can also be used to check - whether this DB instance is still open: it will evaluate to - `undefined` after the DB object's close() method is called. - - In the main window thread, the filenames `":localStorage:"` and - `":sessionStorage:"` are special: they cause the db to use either - localStorage or sessionStorage for storing the database using - the kvvfs. If one of these names are used, they trump - any vfs name set in the arguments. - */ - const DB = function(...args) { - dbCtorHelper.apply(this, args); - }; - DB.dbCtorHelper = dbCtorHelper; - /** - Internal-use enum for mapping JS types to DB-bindable types. - These do not (and need not) line up with the SQLITE_type - values. All values in this enum must be truthy and (mostly) - distinct but they need not be numbers. - */ - const BindTypes = { - null: 1, - number: 2, - string: 3, - boolean: 4, - blob: 5 - }; - if (wasm.bigIntEnabled) BindTypes.bigint = BindTypes.number; - /** - This class wraps sqlite3_stmt. Calling this constructor - directly will trigger an exception. Use DB.prepare() to create - new instances. - - For purposes of passing a Stmt instance to C-style sqlite3 - functions, its read-only `pointer` property holds its `sqlite3_stmt*` - pointer value. - - Other non-function properties include: - - - `db`: the DB object which created the statement. - - - `columnCount`: the number of result columns in the query, or 0 - for queries which cannot return results. This property is a - read-only proxy for sqlite3_column_count() and its use in loops - should be avoided because of the call overhead associated with - that. The `columnCount` is not cached when the Stmt is created - because a schema change made between this statement's preparation - and when it is stepped may invalidate it. - - - `parameterCount`: the number of bindable parameters in the - query. Like `columnCount`, this property is ready-only and is a - proxy for a C API call. - - As a general rule, most methods of this class will throw if - called on an instance which has been finalized. For brevity's - sake, the method docs do not all repeat this warning. - */ - const Stmt = function() { - if (BindTypes !== arguments[2]) toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare()."); - this.db = arguments[0]; - __ptrMap.set(this, arguments[1]); - if (arguments.length > 3 && !arguments[3]) __doesNotOwnHandle.add(this); - }; - /** Throws if the given DB has been closed, else it is returned. */ - const affirmDbOpen = function(db) { - if (!db.pointer) toss3("DB has been closed."); - return db; - }; - /** Throws if ndx is not an integer or if it is out of range - for stmt.columnCount, else returns stmt. - - Reminder: this will also fail after the statement is finalized - but the resulting error will be about an out-of-bounds column - index rather than a statement-is-finalized error. - */ - const affirmColIndex = function(stmt, ndx) { - if (ndx !== (ndx | 0) || ndx < 0 || ndx >= stmt.columnCount) toss3("Column index", ndx, "is out of range."); - return stmt; - }; - /** - Expects to be passed the `arguments` object from DB.exec(). Does - the argument processing/validation, throws on error, and returns - a new object on success: - - { sql: the SQL, opt: optionsObj, cbArg: function} - - The opt object is a normalized copy of any passed to this - function. The sql will be converted to a string if it is provided - in one of the supported non-string formats. - - cbArg is only set if the opt.callback or opt.resultRows are set, - in which case it's a function which expects to be passed the - current Stmt and returns the callback argument of the type - indicated by the input arguments. - */ - const parseExecArgs = function(db, args) { - const out = Object.create(null); - out.opt = Object.create(null); - switch (args.length) { - case 1: - if ("string" === typeof args[0] || util.isSQLableTypedArray(args[0])) out.sql = args[0]; - else if (Array.isArray(args[0])) out.sql = args[0]; - else if (args[0] && "object" === typeof args[0]) { - out.opt = args[0]; - out.sql = out.opt.sql; - } - break; - case 2: - out.sql = args[0]; - out.opt = args[1]; - break; - default: toss3("Invalid argument count for exec()."); - } - out.sql = util.flexibleString(out.sql); - if ("string" !== typeof out.sql) toss3("Missing SQL argument or unsupported SQL value type."); - const opt = out.opt; - switch (opt.returnValue) { - case "resultRows": - if (!opt.resultRows) opt.resultRows = []; - out.returnVal = () => opt.resultRows; - break; - case "saveSql": - if (!opt.saveSql) opt.saveSql = []; - out.returnVal = () => opt.saveSql; - break; - case void 0: - case "this": - out.returnVal = () => db; - break; - default: toss3("Invalid returnValue value:", opt.returnValue); - } - if (!opt.callback && !opt.returnValue && void 0 !== opt.rowMode) { - if (!opt.resultRows) opt.resultRows = []; - out.returnVal = () => opt.resultRows; - } - if (opt.callback || opt.resultRows) switch (void 0 === opt.rowMode ? "array" : opt.rowMode) { - case "object": - out.cbArg = (stmt, cache) => { - if (!cache.columnNames) cache.columnNames = stmt.getColumnNames([]); - const row = stmt.get([]); - const rv = Object.create(null); - for (const i in cache.columnNames) rv[cache.columnNames[i]] = row[i]; - return rv; - }; - break; - case "array": - out.cbArg = (stmt) => stmt.get([]); - break; - case "stmt": - if (Array.isArray(opt.resultRows)) toss3("exec(): invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", "a result column number, or column name reference."); - out.cbArg = (stmt) => stmt; - break; - default: - if (util.isInt32(opt.rowMode)) { - out.cbArg = (stmt) => stmt.get(opt.rowMode); - break; - } else if ("string" === typeof opt.rowMode && opt.rowMode.length > 1 && "$" === opt.rowMode[0]) { - const $colName = opt.rowMode.substr(1); - out.cbArg = (stmt) => { - const rc = stmt.get(Object.create(null))[$colName]; - return void 0 === rc ? toss3(capi.SQLITE_NOTFOUND, "exec(): unknown result column:", $colName) : rc; - }; - break; - } - toss3("Invalid rowMode:", opt.rowMode); - } - return out; - }; - /** - Internal impl of the DB.selectValue(), selectArray(), and - selectObject() methods. - */ - const __selectFirstRow = (db, sql, bind, ...getArgs) => { - const stmt = db.prepare(sql); - try { - const rc = stmt.bind(bind).step() ? stmt.get(...getArgs) : void 0; - stmt.reset(); - return rc; - } finally { - stmt.finalize(); - } - }; - /** - Internal impl of the DB.selectArrays() and selectObjects() - methods. - */ - const __selectAll = (db, sql, bind, rowMode) => db.exec({ - sql, - bind, - rowMode, - returnValue: "resultRows" - }); - /** - Expects to be given a DB instance or an `sqlite3*` pointer (may - be null) and an sqlite3 API result code. If the result code is - not falsy, this function throws an SQLite3Error with an error - message from sqlite3_errmsg(), using db (or, if db is-a DB, - db.pointer) as the db handle, or sqlite3_errstr() if db is - falsy. Note that if it's passed a non-error code like SQLITE_ROW - or SQLITE_DONE, it will still throw but the error string might be - "Not an error." The various non-0 non-error codes need to be - checked for in client code where they are expected. - - The thrown exception's `resultCode` property will be the value of - the second argument to this function. - - If it does not throw, it returns its first argument. - */ - DB.checkRc = (db, resultCode) => checkSqlite3Rc(db, resultCode); - DB.prototype = { - isOpen: function() { - return !!this.pointer; - }, - affirmOpen: function() { - return affirmDbOpen(this); - }, - close: function() { - const pDb = this.pointer; - if (pDb) { - if (this.onclose && this.onclose.before instanceof Function) try { - this.onclose.before(this); - } catch (e) {} - Object.keys(__stmtMap.get(this)).forEach((k, s) => { - if (s && s.pointer) try { - s.finalize(); - } catch (e) {} - }); - __ptrMap.delete(this); - __stmtMap.delete(this); - if (!__doesNotOwnHandle.delete(this)) capi.sqlite3_close_v2(pDb); - if (this.onclose && this.onclose.after instanceof Function) try { - this.onclose.after(this); - } catch (e) {} - delete this.filename; - } - }, - changes: function(total = false, sixtyFour = false) { - const p = affirmDbOpen(this).pointer; - if (total) return sixtyFour ? capi.sqlite3_total_changes64(p) : capi.sqlite3_total_changes(p); - else return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p); - }, - dbFilename: function(dbName = "main") { - return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); - }, - dbName: function(dbNumber = 0) { - return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); - }, - dbVfsName: function(dbName = 0) { - let rc; - const pVfs = capi.sqlite3_js_db_vfs(affirmDbOpen(this).pointer, dbName); - if (pVfs) { - const v = new capi.sqlite3_vfs(pVfs); - try { - rc = wasm.cstrToJs(v.$zName); - } finally { - v.dispose(); - } - } - return rc; - }, - prepare: function(sql) { - affirmDbOpen(this); - const stack = wasm.pstack.pointer; - let ppStmt, pStmt; - try { - ppStmt = wasm.pstack.alloc(8); - DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); - pStmt = wasm.peekPtr(ppStmt); - } finally { - wasm.pstack.restore(stack); - } - if (!pStmt) toss3("Cannot prepare empty SQL."); - const stmt = new Stmt(this, pStmt, BindTypes); - __stmtMap.get(this)[pStmt] = stmt; - return stmt; - }, - exec: function() { - affirmDbOpen(this); - const arg = parseExecArgs(this, arguments); - if (!arg.sql) return toss3("exec() requires an SQL string."); - const opt = arg.opt; - const callback = opt.callback; - const resultRows = Array.isArray(opt.resultRows) ? opt.resultRows : void 0; - let stmt; - let bind = opt.bind; - let evalFirstResult = !!(arg.cbArg || opt.columnNames || resultRows); - const stack = wasm.scopedAllocPush(); - const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : void 0; - try { - const isTA = util.isSQLableTypedArray(arg.sql); - let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); - const ppStmt = wasm.scopedAlloc(2 * wasm.ptr.size + (sqlByteLen + 1)); - const pzTail = wasm.ptr.add(ppStmt, wasm.ptr.size); - let pSql = wasm.ptr.add(pzTail, wasm.ptr.size); - const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen); - if (isTA) wasm.heap8().set(arg.sql, pSql); - else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); - wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0); - while (pSql && wasm.peek8(pSql)) { - wasm.pokePtr([ppStmt, pzTail], 0); - DB.checkRc(this, capi.sqlite3_prepare_v3(this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail)); - const pStmt = wasm.peekPtr(ppStmt); - pSql = wasm.peekPtr(pzTail); - sqlByteLen = Number(wasm.ptr.add(pSqlEnd, -pSql)); - if (!pStmt) continue; - if (saveSql) saveSql.push(capi.sqlite3_sql(pStmt).trim()); - stmt = new Stmt(this, pStmt, BindTypes); - if (bind && stmt.parameterCount) { - stmt.bind(bind); - bind = null; - } - if (evalFirstResult && stmt.columnCount) { - let gotColNames = Array.isArray(opt.columnNames) ? 0 : 1; - evalFirstResult = false; - if (arg.cbArg || resultRows) { - const cbArgCache = Object.create(null); - for (; stmt.step(); __execLock.delete(stmt)) { - if (0 === gotColNames++) stmt.getColumnNames(cbArgCache.columnNames = opt.columnNames || []); - __execLock.add(stmt); - const row = arg.cbArg(stmt, cbArgCache); - if (resultRows) resultRows.push(row); - if (callback && false === callback.call(opt, row, stmt)) break; - } - __execLock.delete(stmt); - } - if (0 === gotColNames) stmt.getColumnNames(opt.columnNames); - } else stmt.step(); - stmt.reset().finalize(); - stmt = null; - } - } finally { - if (stmt) { - __execLock.delete(stmt); - stmt.finalize(); - } - wasm.scopedAllocPop(stack); - } - return arg.returnVal(); - }, - createFunction: function f(name, xFunc, opt) { - const isFunc = (f) => f instanceof Function; - switch (arguments.length) { - case 1: - opt = name; - name = opt.name; - xFunc = opt.xFunc || 0; - break; - case 2: - if (!isFunc(xFunc)) { - opt = xFunc; - xFunc = opt.xFunc || 0; - } - break; - case 3: break; - default: break; - } - if (!opt) opt = {}; - if ("string" !== typeof name) toss3("Invalid arguments: missing function name."); - let xStep = opt.xStep || 0; - let xFinal = opt.xFinal || 0; - const xValue = opt.xValue || 0; - const xInverse = opt.xInverse || 0; - let isWindow = void 0; - if (isFunc(xFunc)) { - isWindow = false; - if (isFunc(xStep) || isFunc(xFinal)) toss3("Ambiguous arguments: scalar or aggregate?"); - xStep = xFinal = null; - } else if (isFunc(xStep)) { - if (!isFunc(xFinal)) toss3("Missing xFinal() callback for aggregate or window UDF."); - xFunc = null; - } else if (isFunc(xFinal)) toss3("Missing xStep() callback for aggregate or window UDF."); - else toss3("Missing function-type properties."); - if (false === isWindow) { - if (isFunc(xValue) || isFunc(xInverse)) toss3("xValue and xInverse are not permitted for non-window UDFs."); - } else if (isFunc(xValue)) { - if (!isFunc(xInverse)) toss3("xInverse must be provided if xValue is."); - isWindow = true; - } else if (isFunc(xInverse)) toss3("xValue must be provided if xInverse is."); - const pApp = opt.pApp; - if (void 0 !== pApp && null !== pApp && !wasm.isPtr(pApp)) toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); - const xDestroy = opt.xDestroy || 0; - if (xDestroy && !isFunc(xDestroy)) toss3("xDestroy property must be a function."); - let fFlags = 0; - if (getOwnOption(opt, "deterministic")) fFlags |= capi.SQLITE_DETERMINISTIC; - if (getOwnOption(opt, "directOnly")) fFlags |= capi.SQLITE_DIRECTONLY; - if (getOwnOption(opt, "innocuous")) fFlags |= capi.SQLITE_INNOCUOUS; - name = name.toLowerCase(); - const xArity = xFunc || xStep; - const arity = getOwnOption(opt, "arity"); - const arityArg = "number" === typeof arity ? arity : xArity.length ? xArity.length - 1 : 0; - let rc; - if (isWindow) rc = capi.sqlite3_create_window_function(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xStep, xFinal, xValue, xInverse, xDestroy); - else rc = capi.sqlite3_create_function_v2(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xFunc, xStep, xFinal, xDestroy); - DB.checkRc(this, rc); - return this; - }, - selectValue: function(sql, bind, asType) { - return __selectFirstRow(this, sql, bind, 0, asType); - }, - selectValues: function(sql, bind, asType) { - const stmt = this.prepare(sql), rc = []; - try { - stmt.bind(bind); - while (stmt.step()) rc.push(stmt.get(0, asType)); - stmt.reset(); - } finally { - stmt.finalize(); - } - return rc; - }, - selectArray: function(sql, bind) { - return __selectFirstRow(this, sql, bind, []); - }, - selectObject: function(sql, bind) { - return __selectFirstRow(this, sql, bind, {}); - }, - selectArrays: function(sql, bind) { - return __selectAll(this, sql, bind, "array"); - }, - selectObjects: function(sql, bind) { - return __selectAll(this, sql, bind, "object"); - }, - openStatementCount: function() { - return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; - }, - transaction: function(callback) { - let opener = "BEGIN"; - if (arguments.length > 1) { - if (/[^a-zA-Z]/.test(arguments[0])) toss3(capi.SQLITE_MISUSE, "Invalid argument for BEGIN qualifier."); - opener += " " + arguments[0]; - callback = arguments[1]; - } - affirmDbOpen(this).exec(opener); - try { - const rc = callback(this); - this.exec("COMMIT"); - return rc; - } catch (e) { - this.exec("ROLLBACK"); - throw e; - } - }, - savepoint: function(callback) { - affirmDbOpen(this).exec("SAVEPOINT oo1"); - try { - const rc = callback(this); - this.exec("RELEASE oo1"); - return rc; - } catch (e) { - this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); - throw e; - } - }, - checkRc: function(resultCode) { - return checkSqlite3Rc(this, resultCode); - } - }; - /** - Returns a new oo1.DB instance which wraps the given (sqlite3*) - WASM pointer, optionally with or without taking over ownership of - that pointer. - - The first argument must be either a non-NULL (sqlite3*) WASM - pointer. - - The second argument, defaulting to false, specifies ownership of - the first argument. If it is truthy, the returned object will - pass that pointer to sqlite3_close() when its close() method is - called, otherwise it will not. - - Throws if pDb is not a non-0 WASM pointer. - - The caller MUST GUARANTEE that the passed-in handle will outlive - the returned object, i.e. that it will not be closed. If it is closed, - this object will hold a stale pointer and results are undefined. - - Aside from its lifetime, the proxy is to be treated as any other - DB instance, including the requirement of calling close() on - it. close() will free up internal resources owned by the proxy - and disassociate the proxy from that handle but will not - actually close the proxied db handle unless this function is - passed a thruthy second argument. - - To stress: - - - DO NOT call sqlite3_close() (or similar) on the being-proxied - pointer while a proxy is active. - - - ALWAYS eventually call close() on the returned object. If the - proxy does not own the underlying handle then its MUST be - closed BEFORE the being-proxied handle is closed. - - Design notes: - - - wrapHandle() "could" accept a DB object instance as its first - argument and proxy thatDb.pointer but there is currently no use - case where doing so would be useful, so it does not allow - that. That restriction may be lifted in a future version. - */ - DB.wrapHandle = function(pDb, takeOwnership = false) { - if (!pDb || !wasm.isPtr(pDb)) throw new sqlite3.SQLite3Error(capi.SQLITE_MISUSE, "Argument must be a WASM sqlite3 pointer"); - return new DB({ - "sqlite3*": pDb, - "sqlite3*:takeOwnership": !!takeOwnership - }); - }; - /** Throws if the given Stmt has been finalized, else stmt is - returned. */ - const affirmStmtOpen = function(stmt) { - if (!stmt.pointer) toss3("Stmt has been closed."); - return stmt; - }; - /** Returns an opaque truthy value from the BindTypes - enum if v's type is a valid bindable type, else - returns a falsy value. As a special case, a value of - undefined is treated as a bind type of null. */ - const isSupportedBindType = function(v) { - let t = BindTypes[null === v || void 0 === v ? "null" : typeof v]; - switch (t) { - case BindTypes.boolean: - case BindTypes.null: - case BindTypes.number: - case BindTypes.string: return t; - case BindTypes.bigint: return wasm.bigIntEnabled ? t : void 0; - default: return util.isBindableTypedArray(v) ? BindTypes.blob : void 0; - } - }; - /** - If isSupportedBindType(v) returns a truthy value, this - function returns that value, else it throws. - */ - const affirmSupportedBindType = function(v) { - return isSupportedBindType(v) || toss3("Unsupported bind() argument type:", typeof v); - }; - /** - If key is a number and within range of stmt's bound parameter - count, key is returned. - - If key is not a number then it must be a JS string (not a WASM - string) and it is checked against named parameters. If a match is - found, its index is returned. - - Else it throws. - */ - const affirmParamIndex = function(stmt, key) { - const n = "number" === typeof key ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); - if (0 === n || !util.isInt32(n)) toss3("Invalid bind() parameter name: " + key); - else if (n < 1 || n > stmt.parameterCount) toss3("Bind index", key, "is out of range."); - return n; - }; - /** - Each Stmt object which is "locked" by DB.exec() gets an entry - here to note that "lock". - - The reason this is in place is because exec({callback:...})'s - callback gets access to the Stmt objects created internally by - exec() but it must not use certain Stmt APIs. - */ - const __execLock = /* @__PURE__ */ new Set(); - /** - This is a Stmt.get() counterpart of __execLock. Each time - Stmt.step() returns true, the statement is added to this set, - indicating that Stmt.get() is legal. Stmt APIs which invalidate - that status remove the Stmt object from this set, which will - cause Stmt.get() to throw with a descriptive error message - instead of a more generic "API misuse" if we were to allow that - call to reach the C API. - */ - const __stmtMayGet = /* @__PURE__ */ new Set(); - /** - Stmt APIs which are prohibited on locked objects must call - affirmNotLockedByExec() before doing any work. - - If __execLock.has(stmt) is truthy, this throws an exception - complaining that the 2nd argument (an operation name, - e.g. "bind()") is not legal while the statement is "locked". - Locking happens before an exec()-like callback is passed a - statement, to ensure that the callback does not mutate or - finalize the statement. If it does not throw, it returns stmt. - */ - const affirmNotLockedByExec = function(stmt, currentOpName) { - if (__execLock.has(stmt)) toss3("Operation is illegal when statement is locked:", currentOpName); - return stmt; - }; - /** - Binds a single bound parameter value on the given stmt at the - given index (numeric or named) using the given bindType (see - the BindTypes enum) and value. Throws on error. Returns stmt on - success. - */ - const bindOne = function f(stmt, ndx, bindType, val) { - affirmNotLockedByExec(affirmStmtOpen(stmt), "bind()"); - if (!f._) { - f._tooBigInt = (v) => toss3("BigInt value is too big to store without precision loss:", v); - f._ = { string: function(stmt, ndx, val, asBlob) { - const [pStr, n] = wasm.allocCString(val, true); - return (asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text)(stmt.pointer, ndx, pStr, n, capi.SQLITE_WASM_DEALLOC); - } }; - } - affirmSupportedBindType(val); - ndx = affirmParamIndex(stmt, ndx); - let rc = 0; - switch (null === val || void 0 === val ? BindTypes.null : bindType) { - case BindTypes.null: - rc = capi.sqlite3_bind_null(stmt.pointer, ndx); - break; - case BindTypes.string: - rc = f._.string(stmt, ndx, val, false); - break; - case BindTypes.number: { - let m; - if (util.isInt32(val)) m = capi.sqlite3_bind_int; - else if ("bigint" === typeof val) if (!util.bigIntFits64(val)) f._tooBigInt(val); - else if (wasm.bigIntEnabled) m = capi.sqlite3_bind_int64; - else if (util.bigIntFitsDouble(val)) { - val = Number(val); - m = capi.sqlite3_bind_double; - } else f._tooBigInt(val); - else { - val = Number(val); - if (wasm.bigIntEnabled && Number.isInteger(val)) m = capi.sqlite3_bind_int64; - else m = capi.sqlite3_bind_double; - } - rc = m(stmt.pointer, ndx, val); - break; - } - case BindTypes.boolean: - rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); - break; - case BindTypes.blob: { - if ("string" === typeof val) { - rc = f._.string(stmt, ndx, val, true); - break; - } else if (val instanceof ArrayBuffer) val = new Uint8Array(val); - else if (!util.isBindableTypedArray(val)) toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, Int8Array, or ArrayBuffer."); - const pBlob = wasm.alloc(val.byteLength || 1); - wasm.heap8().set(val.byteLength ? val : [0], Number(pBlob)); - rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); - break; - } - default: - sqlite3.config.warn("Unsupported bind() argument type:", val); - toss3("Unsupported bind() argument type: " + typeof val); - } - if (rc) DB.checkRc(stmt.db.pointer, rc); - return stmt; - }; - Stmt.prototype = { - finalize: function() { - const ptr = this.pointer; - if (ptr) { - affirmNotLockedByExec(this, "finalize()"); - const rc = __doesNotOwnHandle.delete(this) ? 0 : capi.sqlite3_finalize(ptr); - delete __stmtMap.get(this.db)[ptr]; - __ptrMap.delete(this); - __execLock.delete(this); - __stmtMayGet.delete(this); - delete this.parameterCount; - delete this.db; - return rc; - } - }, - clearBindings: function() { - affirmNotLockedByExec(affirmStmtOpen(this), "clearBindings()"); - capi.sqlite3_clear_bindings(this.pointer); - __stmtMayGet.delete(this); - return this; - }, - reset: function(alsoClearBinds) { - affirmNotLockedByExec(this, "reset()"); - if (alsoClearBinds) this.clearBindings(); - const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer); - __stmtMayGet.delete(this); - checkSqlite3Rc(this.db, rc); - return this; - }, - bind: function() { - affirmStmtOpen(this); - let ndx, arg; - switch (arguments.length) { - case 1: - ndx = 1; - arg = arguments[0]; - break; - case 2: - ndx = arguments[0]; - arg = arguments[1]; - break; - default: toss3("Invalid bind() arguments."); - } - if (void 0 === arg) return this; - else if (!this.parameterCount) toss3("This statement has no bindable parameters."); - __stmtMayGet.delete(this); - if (null === arg) return bindOne(this, ndx, BindTypes.null, arg); - else if (Array.isArray(arg)) { - if (1 !== arguments.length) toss3("When binding an array, an index argument is not permitted."); - arg.forEach((v, i) => bindOne(this, i + 1, affirmSupportedBindType(v), v)); - return this; - } else if (arg instanceof ArrayBuffer) arg = new Uint8Array(arg); - if ("object" === typeof arg && !util.isBindableTypedArray(arg)) { - if (1 !== arguments.length) toss3("When binding an object, an index argument is not permitted."); - Object.keys(arg).forEach((k) => bindOne(this, k, affirmSupportedBindType(arg[k]), arg[k])); - return this; - } else return bindOne(this, ndx, affirmSupportedBindType(arg), arg); - toss3("Should not reach this point."); - }, - bindAsBlob: function(ndx, arg) { - affirmStmtOpen(this); - if (1 === arguments.length) { - arg = ndx; - ndx = 1; - } - const t = affirmSupportedBindType(arg); - if (BindTypes.string !== t && BindTypes.blob !== t && BindTypes.null !== t) toss3("Invalid value type for bindAsBlob()"); - return bindOne(this, ndx, BindTypes.blob, arg); - }, - step: function() { - affirmNotLockedByExec(this, "step()"); - const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); - switch (rc) { - case capi.SQLITE_DONE: - __stmtMayGet.delete(this); - return false; - case capi.SQLITE_ROW: - __stmtMayGet.add(this); - return true; - default: - __stmtMayGet.delete(this); - sqlite3.config.warn("sqlite3_step() rc=", rc, capi.sqlite3_js_rc_str(rc), "SQL =", capi.sqlite3_sql(this.pointer)); - DB.checkRc(this.db.pointer, rc); - } - }, - stepReset: function() { - this.step(); - return this.reset(); - }, - stepFinalize: function() { - try { - const rc = this.step(); - this.reset(); - return rc; - } finally { - try { - this.finalize(); - } catch (e) {} - } - }, - get: function(ndx, asType) { - if (!__stmtMayGet.has(affirmStmtOpen(this))) toss3("Stmt.step() has not (recently) returned true."); - if (Array.isArray(ndx)) { - let i = 0; - const n = this.columnCount; - while (i < n) ndx[i] = this.get(i++); - return ndx; - } else if (ndx && "object" === typeof ndx) { - let i = 0; - const n = this.columnCount; - while (i < n) ndx[capi.sqlite3_column_name(this.pointer, i)] = this.get(i++); - return ndx; - } - affirmColIndex(this, ndx); - switch (void 0 === asType ? capi.sqlite3_column_type(this.pointer, ndx) : asType) { - case capi.SQLITE_NULL: return null; - case capi.SQLITE_INTEGER: if (wasm.bigIntEnabled) { - const rc = capi.sqlite3_column_int64(this.pointer, ndx); - if (rc >= Number.MIN_SAFE_INTEGER && rc <= Number.MAX_SAFE_INTEGER) return Number(rc).valueOf(); - return rc; - } else { - const rc = capi.sqlite3_column_double(this.pointer, ndx); - if (rc > Number.MAX_SAFE_INTEGER || rc < Number.MIN_SAFE_INTEGER) toss3("Integer is out of range for JS integer range: " + rc); - return util.isInt32(rc) ? rc | 0 : rc; - } - case capi.SQLITE_FLOAT: return capi.sqlite3_column_double(this.pointer, ndx); - case capi.SQLITE_TEXT: return capi.sqlite3_column_text(this.pointer, ndx); - case capi.SQLITE_BLOB: { - const n = capi.sqlite3_column_bytes(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx), rc = new Uint8Array(n); - if (n) { - rc.set(wasm.heap8u().slice(Number(ptr), Number(ptr) + n), 0); - if (this.db._blobXfer instanceof Array) this.db._blobXfer.push(rc.buffer); - } - return rc; - } - default: toss3("Don't know how to translate", "type of result column #" + ndx + "."); - } - toss3("Not reached."); - }, - getInt: function(ndx) { - return this.get(ndx, capi.SQLITE_INTEGER); - }, - getFloat: function(ndx) { - return this.get(ndx, capi.SQLITE_FLOAT); - }, - getString: function(ndx) { - return this.get(ndx, capi.SQLITE_TEXT); - }, - getBlob: function(ndx) { - return this.get(ndx, capi.SQLITE_BLOB); - }, - getJSON: function(ndx) { - const s = this.get(ndx, capi.SQLITE_STRING); - return null === s ? s : JSON.parse(s); - }, - getColumnName: function(ndx) { - return capi.sqlite3_column_name(affirmColIndex(affirmStmtOpen(this), ndx).pointer, ndx); - }, - getColumnNames: function(tgt = []) { - affirmColIndex(affirmStmtOpen(this), 0); - const n = this.columnCount; - for (let i = 0; i < n; ++i) tgt.push(capi.sqlite3_column_name(this.pointer, i)); - return tgt; - }, - getParamIndex: function(name) { - return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_index(this.pointer, name) : void 0; - }, - getParamName: function(ndx) { - return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_name(this.pointer, ndx) : void 0; - }, - isBusy: function() { - return 0 !== capi.sqlite3_stmt_busy(affirmStmtOpen(this)); - }, - isReadOnly: function() { - return 0 !== capi.sqlite3_stmt_readonly(affirmStmtOpen(this)); - } - }; - { - const prop = { - enumerable: true, - get: function() { - return __ptrMap.get(this); - }, - set: () => toss3("The pointer property is read-only.") - }; - Object.defineProperty(Stmt.prototype, "pointer", prop); - Object.defineProperty(DB.prototype, "pointer", prop); - } - /** - Stmt.columnCount is an interceptor for sqlite3_column_count(). - - This requires an unfortunate performance hit compared to caching - columnCount when the Stmt is created/prepared (as was done in - SQLite <=3.42.0), but is necessary in order to handle certain - corner cases, as described in - https://sqlite.org/forum/forumpost/7774b773937cbe0a. - */ - Object.defineProperty(Stmt.prototype, "columnCount", { - enumerable: false, - get: function() { - return capi.sqlite3_column_count(this.pointer); - }, - set: () => toss3("The columnCount property is read-only.") - }); - Object.defineProperty(Stmt.prototype, "parameterCount", { - enumerable: false, - get: function() { - return capi.sqlite3_bind_parameter_count(this.pointer); - }, - set: () => toss3("The parameterCount property is read-only.") - }); - /** - The Stmt counterpart of oo1.DB.wrapHandle(), this creates a Stmt - instance which wraps a WASM (sqlite3_stmt*) in the oo1 API, - optionally with or without taking over ownership of that pointer. - - The first argument must be an oo1.DB instance[^1]. - - The second argument must be a valid WASM (sqlite3_stmt*), as - produced by sqlite3_prepare_v2() and sqlite3_prepare_v3(). - - The third argument, defaulting to false, specifies whether the - returned Stmt object takes over ownership of the underlying - (sqlite3_stmt*). If true, the returned object's finalize() method - will finalize that handle, else it will not. If it is false, - ownership of pStmt is unchanged and pStmt MUST outlive the - returned object or results are undefined. - - This function throws if the arguments are invalid. On success it - returns a new Stmt object which wraps the given statement - pointer. - - Like all Stmt objects, the finalize() method must eventually be - called on the returned object to free up internal resources, - regardless of whether this function's third argument is true or - not. - - [^1]: The first argument cannot be a (sqlite3*) because the - resulting Stmt object requires a parent DB object. It is not yet - determined whether it would be of general benefit to refactor the - DB/Stmt pair internals to communicate in terms of the underlying - (sqlite3*) rather than a DB object. If so, we could laxen the - first argument's requirement and allow an (sqlite3*). Because - DB.wrapHandle() enables multiple DB objects to proxy the same - (sqlite3*), we cannot unambiguously translate the first arugment - from (sqlite3*) to DB instances for us with this function's first - argument. - */ - Stmt.wrapHandle = function(oo1db, pStmt, takeOwnership = false) { - if (!(oo1db instanceof DB) || !oo1db.pointer) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "First argument must be an opened sqlite3.oo1.DB instance"); - if (!pStmt || !wasm.isPtr(pStmt)) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "Second argument must be a WASM sqlite3_stmt pointer"); - return new Stmt(oo1db, pStmt, BindTypes, !!takeOwnership); - }; - /** The OO API's public namespace. */ - sqlite3.oo1 = { - DB, - Stmt - }; - }); - /** - 2022-07-22 - - The author disclaims copyright to this source code. In place of a - legal notice, here is a blessing: - - * May you do good and not evil. - * May you find forgiveness for yourself and forgive others. - * May you share freely, never taking more than you give. - - *********************************************************************** - - This file implements the initializer for SQLite's "Worker API #1", a - very basic DB access API intended to be scripted from a main window - thread via Worker-style messages. Because of limitations in that - type of communication, this API is minimalistic and only capable of - serving relatively basic DB requests (e.g. it cannot process nested - query loops concurrently). - - This file requires that the core C-style sqlite3 API and OO API #1 - have been loaded. - */ - /** - sqlite3.initWorker1API() implements a Worker-based wrapper around - SQLite3 OO API #1, colloquially known as "Worker API #1". - - In order to permit this API to be loaded in worker threads without - automatically registering onmessage handlers, initializing the - worker API requires calling initWorker1API(). If this function is - called from a non-worker thread then it throws an exception. It - must only be called once per Worker. - - When initialized, it installs message listeners to receive Worker - messages and then it posts a message in the form: - - ``` - {type:'sqlite3-api', result:'worker1-ready'} - ``` - - to let the client know that it has been initialized. Clients may - optionally depend on this function not returning until - initialization is complete, as the initialization is synchronous. - In some contexts, however, listening for the above message is - a better fit. - - Note that the worker-based interface can be slightly quirky because - of its async nature. In particular, any number of messages may be posted - to the worker before it starts handling any of them. If, e.g., an - "open" operation fails, any subsequent messages will fail. The - Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`) - is more comfortable to use in that regard. - - The documentation for the input and output worker messages for - this API follows... - - ==================================================================== - Common message format... - - Each message posted to the worker has an operation-independent - envelope and operation-dependent arguments: - - ``` - { - type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get' - - messageId: OPTIONAL arbitrary value. The worker will copy it as-is - into response messages to assist in client-side dispatching. - - dbId: a db identifier string (returned by 'open') which tells the - operation which database instance to work on. If not provided, the - first-opened db is used. This is an "opaque" value, with no - inherently useful syntax or information. Its value is subject to - change with any given build of this API and cannot be used as a - basis for anything useful beyond its one intended purpose. - - args: ...operation-dependent arguments... - - // the framework may add other properties for testing or debugging - // purposes. - - } - ``` - - Response messages, posted back to the main thread, look like: - - ``` - { - type: string. Same as above except for error responses, which have the type - 'error', - - messageId: same value, if any, provided by the inbound message - - dbId: the id of the db which was operated on, if any, as returned - by the corresponding 'open' operation. - - result: ...operation-dependent result... - - } - ``` - - ==================================================================== - Error responses - - Errors are reported messages in an operation-independent format: - - ``` - { - type: "error", - - messageId: ...as above..., - - dbId: ...as above... - - result: { - - operation: type of the triggering operation: 'open', 'close', ... - - message: ...error message text... - - errorClass: string. The ErrorClass.name property from the thrown exception. - - input: the message object which triggered the error. - - stack: _if available_, a stack trace array. - - } - - } - ``` - - - ==================================================================== - "config-get" - - This operation fetches the serializable parts of the sqlite3 API - configuration. - - Message format: - - ``` - { - type: "config-get", - messageId: ...as above..., - args: currently ignored and may be elided. - } - ``` - - Response: - - ``` - { - type: "config-get", - messageId: ...as above..., - result: { - - version: sqlite3.version object - - bigIntEnabled: bool. True if BigInt support is enabled. - - vfsList: result of sqlite3.capi.sqlite3_js_vfs_list() - } - } - ``` - - - ==================================================================== - "open" a database - - Message format: - - ``` - { - type: "open", - messageId: ...as above..., - args:{ - - filename [=":memory:" or "" (unspecified)]: the db filename. - See the sqlite3.oo1.DB constructor for peculiarities and - transformations, - - vfs: sqlite3_vfs name. Ignored if filename is ":memory:" or "". - This may change how the given filename is resolved. - } - } - ``` - - Response: - - ``` - { - type: "open", - messageId: ...as above..., - result: { - filename: db filename, possibly differing from the input. - - dbId: an opaque ID value which must be passed in the message - envelope to other calls in this API to tell them which db to - use. If it is not provided to future calls, they will default to - operating on the least-recently-opened db. This property is, for - API consistency's sake, also part of the containing message - envelope. Only the `open` operation includes it in the `result` - property. - - persistent: true if the given filename resides in the - known-persistent storage, else false. - - vfs: name of the VFS the "main" db is using. - } - } - ``` - - ==================================================================== - "close" a database - - Message format: - - ``` - { - type: "close", - messageId: ...as above... - dbId: ...as above... - args: OPTIONAL {unlink: boolean} - } - ``` - - If the `dbId` does not refer to an opened ID, this is a no-op. If - the `args` object contains a truthy `unlink` value then the database - will be unlinked (deleted) after closing it. The inability to close a - db (because it's not opened) or delete its file does not trigger an - error. - - Response: - - ``` - { - type: "close", - messageId: ...as above..., - result: { - - filename: filename of closed db, or undefined if no db was closed - - } - } - ``` - - ==================================================================== - "exec" SQL - - All SQL execution is processed through the exec operation. It offers - most of the features of the oo1.DB.exec() method, with a few limitations - imposed by the state having to cross thread boundaries. - - Message format: - - ``` - { - type: "exec", - messageId: ...as above... - dbId: ...as above... - args: string (SQL) or {... see below ...} - } - ``` - - Response: - - ``` - { - type: "exec", - messageId: ...as above..., - dbId: ...as above... - result: { - input arguments, possibly modified. See below. - } - } - ``` - - The arguments are in the same form accepted by oo1.DB.exec(), with - the exceptions noted below. - - If `args.countChanges` (added in version 3.43) is truthy then the - `result` property contained by the returned object will have a - `changeCount` property which holds the number of changes made by the - provided SQL. Because the SQL may contain an arbitrary number of - statements, the `changeCount` is calculated by calling - `sqlite3_total_changes()` before and after the SQL is evaluated. If - the value of `countChanges` is 64 then the `changeCount` property - will be returned as a 64-bit integer in the form of a BigInt (noting - that that will trigger an exception if used in a BigInt-incapable - build). In the latter case, the number of changes is calculated by - calling `sqlite3_total_changes64()` before and after the SQL is - evaluated. - - If the `args.lastInsertRowId` (added in version 3.50.0) is truthy - then the `result` property contained by the returned object will - have a `lastInsertRowId` will hold a BigInt-type value corresponding - to the result of sqlite3_last_insert_rowid(). This value is only - fetched once, after the SQL is run, regardless of how many - statements the SQL contains. This API has no idea whether the SQL - contains any INSERTs, so it is up to the client to apply/rely on - this property only when it makes sense to do so. - - A function-type args.callback property cannot cross - the window/Worker boundary, so is not useful here. If - args.callback is a string then it is assumed to be a - message type key, in which case a callback function will be - applied which posts each row result via: - - postMessage({type: thatKeyType, - rowNumber: 1-based-#, - row: theRow, - columnNames: anArray - }) - - And, at the end of the result set (whether or not any result rows - were produced), it will post an identical message with - (row=undefined, rowNumber=null) to alert the caller than the result - set is completed. Note that a row value of `null` is a legal row - result for certain arg.rowMode values. - - (Design note: we don't use (row=undefined, rowNumber=undefined) to - indicate end-of-results because fetching those would be - indistinguishable from fetching from an empty object unless the - client used hasOwnProperty() (or similar) to distinguish "missing - property" from "property with the undefined value". Similarly, - `null` is a legal value for `row` in some case , whereas the db - layer won't emit a result value of `undefined`.) - - The callback proxy must not recurse into this interface. An exec() - call will tie up the Worker thread, causing any recursion attempt - to wait until the first exec() is completed. - - The response is the input options object (or a synthesized one if - passed only a string), noting that options.resultRows and - options.columnNames may be populated by the call to db.exec(). - - - ==================================================================== - "export" the current db - - To export the underlying database as a byte array... - - Message format: - - ``` - { - type: "export", - messageId: ...as above..., - dbId: ...as above... - } - ``` - - Response: - - ``` - { - type: "export", - messageId: ...as above..., - dbId: ...as above... - result: { - byteArray: Uint8Array (as per sqlite3_js_db_export()), - filename: the db filename, - mimetype: "application/x-sqlite3" - } - } - ``` - - */ - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - const util = sqlite3.util; - sqlite3.initWorker1API = function() { - "use strict"; - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - if (!(globalThis.WorkerGlobalScope instanceof Function)) toss("initWorker1API() must be run from a Worker thread."); - const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); - const DB = sqlite3.oo1.DB; - /** - Returns the app-wide unique ID for the given db, creating one if - needed. - */ - const getDbId = function(db) { - let id = wState.idMap.get(db); - if (id) return id; - id = "db#" + ++wState.idSeq + ":" + Math.floor(Math.random() * 1e8) + ":" + Math.floor(Math.random() * 1e8); - /** ^^^ can't simply use db.pointer b/c closing/opening may re-use - the same address, which could map pending messages to a wrong - instance. - - 2025-07: https://github.com/sqlite/sqlite-wasm/issues/113 - demonstrates that two Worker1s can end up with the same IDs, - despite using different instances of the library, so we need - to add some randomness to the IDs instead of relying on the - pointer addresses. - */ - wState.idMap.set(db, id); - return id; - }; - /** - Internal helper for managing Worker-level state. - */ - const wState = { - dbList: [], - idSeq: 0, - idMap: /* @__PURE__ */ new WeakMap(), - xfer: [], - open: function(opt) { - const db = new DB(opt); - this.dbs[getDbId(db)] = db; - if (this.dbList.indexOf(db) < 0) this.dbList.push(db); - return db; - }, - close: function(db, alsoUnlink) { - if (db) { - delete this.dbs[getDbId(db)]; - const filename = db.filename; - const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0); - db.close(); - const ddNdx = this.dbList.indexOf(db); - if (ddNdx >= 0) this.dbList.splice(ddNdx, 1); - if (alsoUnlink && filename && pVfs) util.sqlite3__wasm_vfs_unlink(pVfs, filename); - } - }, - post: function(msg, xferList) { - if (xferList && xferList.length) { - globalThis.postMessage(msg, Array.from(xferList)); - xferList.length = 0; - } else globalThis.postMessage(msg); - }, - dbs: Object.create(null), - getDb: function(id, require = true) { - return this.dbs[id] || (require ? toss("Unknown (or closed) DB ID:", id) : void 0); - } - }; - /** Throws if the given db is falsy or not opened, else returns its - argument. */ - const affirmDbOpen = function(db = wState.dbList[0]) { - return db && db.pointer ? db : toss("DB is not opened."); - }; - /** Extract dbId from the given message payload. */ - const getMsgDb = function(msgData, affirmExists = true) { - const db = wState.getDb(msgData.dbId, false) || wState.dbList[0]; - return affirmExists ? affirmDbOpen(db) : db; - }; - const getDefaultDbId = function() { - return wState.dbList[0] && getDbId(wState.dbList[0]); - }; - /** - A level of "organizational abstraction" for the Worker1 - API. Each method in this object must map directly to a Worker1 - message type key. The onmessage() dispatcher attempts to - dispatch all inbound messages to a method of this object, - passing it the event.data part of the inbound event object. All - methods must return a plain Object containing any result - state, which the dispatcher may amend. All methods must throw - on error. - */ - const wMsgHandler = { - open: function(ev) { - const oargs = Object.create(null), args = ev.args || Object.create(null); - if (args.simulateError) toss("Throwing because of simulateError flag."); - const rc = Object.create(null); - oargs.vfs = args.vfs; - oargs.filename = args.filename || ""; - const db = wState.open(oargs); - rc.filename = db.filename; - rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); - rc.dbId = getDbId(db); - rc.vfs = db.dbVfsName(); - return rc; - }, - close: function(ev) { - const db = getMsgDb(ev, false); - const response = { filename: db && db.filename }; - if (db) { - const doUnlink = ev.args && "object" === typeof ev.args ? !!ev.args.unlink : false; - wState.close(db, doUnlink); - } - return response; - }, - exec: function(ev) { - const rc = "string" === typeof ev.args ? { sql: ev.args } : ev.args || Object.create(null); - if ("stmt" === rc.rowMode) toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); - else if (!rc.sql) toss("'exec' requires input SQL."); - const db = getMsgDb(ev); - if (rc.callback || Array.isArray(rc.resultRows)) db._blobXfer = wState.xfer; - const theCallback = rc.callback; - let rowNumber = 0; - const hadColNames = !!rc.columnNames; - if ("string" === typeof theCallback) { - if (!hadColNames) rc.columnNames = []; - rc.callback = function(row, stmt) { - wState.post({ - type: theCallback, - columnNames: rc.columnNames, - rowNumber: ++rowNumber, - row - }, wState.xfer); - }; - } - try { - const changeCount = !!rc.countChanges ? db.changes(true, 64 === rc.countChanges) : void 0; - db.exec(rc); - if (void 0 !== changeCount) rc.changeCount = db.changes(true, 64 === rc.countChanges) - changeCount; - const lastInsertRowId = !!rc.lastInsertRowId ? sqlite3.capi.sqlite3_last_insert_rowid(db) : void 0; - if (void 0 !== lastInsertRowId) rc.lastInsertRowId = lastInsertRowId; - if (rc.callback instanceof Function) { - rc.callback = theCallback; - wState.post({ - type: theCallback, - columnNames: rc.columnNames, - rowNumber: null, - row: void 0 - }); - } - } finally { - delete db._blobXfer; - if (rc.callback) rc.callback = theCallback; - } - return rc; - }, - "config-get": function() { - const rc = Object.create(null), src = sqlite3.config; - ["bigIntEnabled"].forEach(function(k) { - if (Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; - }); - rc.version = sqlite3.version; - rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list(); - return rc; - }, - export: function(ev) { - const db = getMsgDb(ev); - const response = { - byteArray: sqlite3.capi.sqlite3_js_db_export(db.pointer), - filename: db.filename, - mimetype: "application/x-sqlite3" - }; - wState.xfer.push(response.byteArray.buffer); - return response; - }, - toss: function(ev) { - toss("Testing worker exception"); - } - }; - globalThis.onmessage = async function(ev) { - ev = ev.data; - let result, dbId = ev.dbId, evType = ev.type; - const arrivalTime = performance.now(); - try { - if (wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function) result = await wMsgHandler[evType](ev); - else toss("Unknown db worker message type:", ev.type); - } catch (err) { - evType = "error"; - result = { - operation: ev.type, - message: err.message, - errorClass: err.name, - input: ev - }; - if (err.stack) result.stack = "string" === typeof err.stack ? err.stack.split(/\n\s*/) : err.stack; - } - if (!dbId) dbId = result.dbId || getDefaultDbId(); - wState.post({ - type: evType, - dbId, - messageId: ev.messageId, - workerReceivedTime: arrivalTime, - workerRespondTime: performance.now(), - departureTime: ev.departureTime, - result - }, wState.xfer); - }; - globalThis.postMessage({ - type: "sqlite3-api", - result: "worker1-ready" - }); - }.bind({ sqlite3 }); - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; - const vfs = Object.create(null); - sqlite3.vfs = vfs; - /** - Uses sqlite3_vfs_register() to register this - sqlite3.capi.sqlite3_vfs instance. This object must have already - been filled out properly. If the first argument is truthy, the - VFS is registered as the default VFS, else it is not. - - On success, returns this object. Throws on error. - */ - capi.sqlite3_vfs.prototype.registerVfs = function(asDefault = false) { - if (!(this instanceof sqlite3.capi.sqlite3_vfs)) toss("Expecting a sqlite3_vfs-type argument."); - const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0); - if (rc) toss("sqlite3_vfs_register(", this, ") failed with rc", rc); - if (this.pointer !== capi.sqlite3_vfs_find(this.$zName)) toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", this); - return this; - }; - /** - A wrapper for - sqlite3.StructBinder.StructType.prototype.installMethods() or - registerVfs() to reduce installation of a VFS and/or its I/O - methods to a single call. - - Accepts an object which contains the properties "io" and/or - "vfs", each of which is itself an object with following properties: - - - `struct`: an sqlite3.StructBinder.StructType-type struct. This - must be a populated (except for the methods) object of type - sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the - "vfs" entry). - - - `methods`: an object mapping sqlite3_io_methods method names - (e.g. 'xClose') to JS implementations of those methods. The JS - implementations must be call-compatible with their native - counterparts. - - For each of those object, this function passes its (`struct`, - `methods`, (optional) `applyArgcCheck`) properties to - installMethods(). - - If the `vfs` entry is set then: - - - Its `struct` property's registerVfs() is called. The - `vfs` entry may optionally have an `asDefault` property, which - gets passed as the argument to registerVfs(). - - - If `struct.$zName` is falsy and the entry has a string-type - `name` property, `struct.$zName` is set to the C-string form of - that `name` value before registerVfs() is called. That string - gets added to the on-dispose state of the struct. - - On success returns this object. Throws on error. - */ - vfs.installVfs = function(opt) { - let count = 0; - const propList = ["io", "vfs"]; - for (const key of propList) { - const o = opt[key]; - if (o) { - ++count; - o.struct.installMethods(o.methods, !!o.applyArgcCheck); - if ("vfs" === key) { - if (!o.struct.$zName && "string" === typeof o.name) o.struct.addOnDispose(o.struct.$zName = wasm.allocCString(o.name)); - o.struct.registerVfs(!!o.asDefault); - } - } - } - if (!count) toss("Misuse: installVfs() options object requires at least", "one of:", propList); - return this; - }; - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - if (!sqlite3.wasm.exports.sqlite3_declare_vtab) return; - const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; - const vtab = Object.create(null); - sqlite3.vtab = vtab; - const sii = capi.sqlite3_index_info; - /** - If n is >=0 and less than this.$nConstraint, this function - returns either a WASM pointer to the 0-based nth entry of - this.$aConstraint (if passed a truthy 2nd argument) or an - sqlite3_index_info.sqlite3_index_constraint object wrapping that - address (if passed a falsy value or no 2nd argument). Returns a - falsy value if n is out of range. - */ - sii.prototype.nthConstraint = function(n, asPtr = false) { - if (n < 0 || n >= this.$nConstraint) return false; - const ptr = wasm.ptr.add(this.$aConstraint, sii.sqlite3_index_constraint.structInfo.sizeof * n); - return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr); - }; - /** - Works identically to nthConstraint() but returns state from - this.$aConstraintUsage, so returns an - sqlite3_index_info.sqlite3_index_constraint_usage instance - if passed no 2nd argument or a falsy 2nd argument. - */ - sii.prototype.nthConstraintUsage = function(n, asPtr = false) { - if (n < 0 || n >= this.$nConstraint) return false; - const ptr = wasm.ptr.add(this.$aConstraintUsage, sii.sqlite3_index_constraint_usage.structInfo.sizeof * n); - return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr); - }; - /** - If n is >=0 and less than this.$nOrderBy, this function - returns either a WASM pointer to the 0-based nth entry of - this.$aOrderBy (if passed a truthy 2nd argument) or an - sqlite3_index_info.sqlite3_index_orderby object wrapping that - address (if passed a falsy value or no 2nd argument). Returns a - falsy value if n is out of range. - */ - sii.prototype.nthOrderBy = function(n, asPtr = false) { - if (n < 0 || n >= this.$nOrderBy) return false; - const ptr = wasm.ptr.add(this.$aOrderBy, sii.sqlite3_index_orderby.structInfo.sizeof * n); - return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr); - }; - /** - Internal factory function for xVtab and xCursor impls. - */ - const __xWrapFactory = function(methodName, StructType) { - return function(ptr, removeMapping = false) { - if (0 === arguments.length) ptr = new StructType(); - if (ptr instanceof StructType) { - this.set(ptr.pointer, ptr); - return ptr; - } else if (!wasm.isPtr(ptr)) sqlite3.SQLite3Error.toss("Invalid argument to", methodName + "()"); - let rc = this.get(ptr); - if (removeMapping) this.delete(ptr); - return rc; - }.bind(/* @__PURE__ */ new Map()); - }; - /** - A factory function which implements a simple lifetime manager for - mappings between C struct pointers and their JS-level wrappers. - The first argument must be the logical name of the manager - (e.g. 'xVtab' or 'xCursor'), which is only used for error - reporting. The second must be the capi.XYZ struct-type value, - e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor. - - Returns an object with 4 methods: create(), get(), unget(), and - dispose(), plus a StructType member with the value of the 2nd - argument. The methods are documented in the body of this - function. - */ - const StructPtrMapper = function(name, StructType) { - const __xWrap = __xWrapFactory(name, StructType); - /** - This object houses a small API for managing mappings of (`T*`) - to StructType objects, specifically within the lifetime - requirements of sqlite3_module methods. - */ - return Object.assign(Object.create(null), { - StructType, - create: (ppOut) => { - const rc = __xWrap(); - wasm.pokePtr(ppOut, rc.pointer); - return rc; - }, - get: (pCObj) => __xWrap(pCObj), - unget: (pCObj) => __xWrap(pCObj, true), - dispose: (pCObj) => __xWrap(pCObj, true)?.dispose?.() - }); - }; - /** - A lifetime-management object for mapping `sqlite3_vtab*` - instances in sqlite3_module methods to capi.sqlite3_vtab - objects. - - The API docs are in the API-internal StructPtrMapper(). - */ - vtab.xVtab = StructPtrMapper("xVtab", capi.sqlite3_vtab); - /** - A lifetime-management object for mapping `sqlite3_vtab_cursor*` - instances in sqlite3_module methods to capi.sqlite3_vtab_cursor - objects. - - The API docs are in the API-internal StructPtrMapper(). - */ - vtab.xCursor = StructPtrMapper("xCursor", capi.sqlite3_vtab_cursor); - /** - Convenience form of creating an sqlite3_index_info wrapper, - intended for use in xBestIndex implementations. Note that the - caller is expected to call dispose() on the returned object - before returning. Though not _strictly_ required, as that object - does not own the pIdxInfo memory, it is nonetheless good form. - */ - vtab.xIndexInfo = (pIdxInfo) => new capi.sqlite3_index_info(pIdxInfo); - /** - Given an sqlite3_module method name and error object, this - function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof - sqlite3.WasmAllocError), else it returns its second argument. Its - intended usage is in the methods of a sqlite3_vfs or - sqlite3_module: - - ``` - try{ - let rc = ... - return rc; - }catch(e){ - return sqlite3.vtab.xError( - 'xColumn', e, sqlite3.capi.SQLITE_XYZ); - // where SQLITE_XYZ is some call-appropriate result code. - } - ``` - - If no 3rd argument is provided, its default depends on - the error type: - - - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM. - - - If err is an SQLite3Error then its `resultCode` property - is used. - - - If all else fails, capi.SQLITE_ERROR is used. - - If xError.errorReporter is a function, it is called in - order to report the error, else the error is not reported. - If that function throws, that exception is ignored. - */ - vtab.xError = function f(methodName, err, defaultRc) { - if (f.errorReporter instanceof Function) try { - f.errorReporter("sqlite3_module::" + methodName + "(): " + err.message); - } catch (e) {} - let rc; - if (err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM; - else if (arguments.length > 2) rc = defaultRc; - else if (err instanceof sqlite3.SQLite3Error) rc = err.resultCode; - return rc || capi.SQLITE_ERROR; - }; - vtab.xError.errorReporter = sqlite3.config.error.bind(sqlite3.config); - /** - A helper for sqlite3_vtab::xRowid() and xUpdate() - implementations. It must be passed the final argument to one of - those methods (an output pointer to an int64 row ID) and the - value to store at the output pointer's address. Returns the same - as wasm.poke() and will throw if the 1st or 2nd arguments - are invalid for that function. - - Example xRowid impl: - - ``` - const xRowid = (pCursor, ppRowid64)=>{ - const c = vtab.xCursor(pCursor); - vtab.xRowid(ppRowid64, c.myRowId); - return 0; - }; - ``` - */ - vtab.xRowid = (ppRowid64, value) => wasm.poke(ppRowid64, value, "i64"); - /** - A helper to initialize and set up an sqlite3_module object for - later installation into individual databases using - sqlite3_create_module(). Requires an object with the following - properties: - - - `methods`: an object containing a mapping of properties with - the C-side names of the sqlite3_module methods, e.g. xCreate, - xBestIndex, etc., to JS implementations for those functions. - Certain special-case handling is performed, as described below. - - - `catchExceptions` (default=false): if truthy, the given methods - are not mapped as-is, but are instead wrapped inside wrappers - which translate exceptions into result codes of SQLITE_ERROR or - SQLITE_NOMEM, depending on whether the exception is an - sqlite3.WasmAllocError. In the case of the xConnect and xCreate - methods, the exception handler also sets the output error - string to the exception's error string. - - - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If - not set, one will be created automatically. If the current - "this" is-a sqlite3_module then it is unconditionally used in - place of `struct`. - - - OPTIONAL `iVersion`: if set, it must be an integer value and it - gets assigned to the `$iVersion` member of the struct object. - If it's _not_ set, and the passed-in `struct` object's `$iVersion` - is 0 (the default) then this function attempts to define a value - for that property based on the list of methods it has. - - If `catchExceptions` is false, it is up to the client to ensure - that no exceptions escape the methods, as doing so would move - them through the C API, leading to undefined - behavior. (vtab.xError() is intended to assist in reporting - such exceptions.) - - Certain methods may refer to the same implementation. To simplify - the definition of such methods: - - - If `methods.xConnect` is `true` then the value of - `methods.xCreate` is used in its place, and vice versa. sqlite - treats xConnect/xCreate functions specially if they are exactly - the same function (same pointer value). - - - If `methods.xDisconnect` is true then the value of - `methods.xDestroy` is used in its place, and vice versa. - - This is to facilitate creation of those methods inline in the - passed-in object without requiring the client to explicitly get a - reference to one of them in order to assign it to the other - one. - - The `catchExceptions`-installed handlers will account for - identical references to the above functions and will install the - same wrapper function for both. - - The given methods are expected to return integer values, as - expected by the C API. If `catchExceptions` is truthy, the return - value of the wrapped function will be used as-is and will be - translated to 0 if the function returns a falsy value (e.g. if it - does not have an explicit return). If `catchExceptions` is _not_ - active, the method implementations must explicitly return integer - values. - - Throws on error. On success, returns the sqlite3_module object - (`this` or `opt.struct` or a new sqlite3_module instance, - depending on how it's called). - */ - vtab.setupModule = function(opt) { - let createdMod = false; - const mod = this instanceof capi.sqlite3_module ? this : opt.struct || (createdMod = new capi.sqlite3_module()); - try { - const methods = opt.methods || toss("Missing 'methods' object."); - for (const e of Object.entries({ - xConnect: "xCreate", - xDisconnect: "xDestroy" - })) { - const k = e[0], v = e[1]; - if (true === methods[k]) methods[k] = methods[v]; - else if (true === methods[v]) methods[v] = methods[k]; - } - if (opt.catchExceptions) { - const fwrap = function(methodName, func) { - if (["xConnect", "xCreate"].indexOf(methodName) >= 0) return function(pDb, pAux, argc, argv, ppVtab, pzErr) { - try { - return func(...arguments) || 0; - } catch (e) { - if (!(e instanceof sqlite3.WasmAllocError)) { - wasm.dealloc(wasm.peekPtr(pzErr)); - wasm.pokePtr(pzErr, wasm.allocCString(e.message)); - } - return vtab.xError(methodName, e); - } - }; - else return function(...args) { - try { - return func(...args) || 0; - } catch (e) { - return vtab.xError(methodName, e); - } - }; - }; - const mnames = [ - "xCreate", - "xConnect", - "xBestIndex", - "xDisconnect", - "xDestroy", - "xOpen", - "xClose", - "xFilter", - "xNext", - "xEof", - "xColumn", - "xRowid", - "xUpdate", - "xBegin", - "xSync", - "xCommit", - "xRollback", - "xFindFunction", - "xRename", - "xSavepoint", - "xRelease", - "xRollbackTo", - "xShadowName" - ]; - const remethods = Object.create(null); - for (const k of mnames) { - const m = methods[k]; - if (!(m instanceof Function)) continue; - else if ("xConnect" === k && methods.xCreate === m) remethods[k] = methods.xCreate; - else if ("xCreate" === k && methods.xConnect === m) remethods[k] = methods.xConnect; - else remethods[k] = fwrap(k, m); - } - mod.installMethods(remethods, false); - } else mod.installMethods(methods, !!opt.applyArgcCheck); - if (0 === mod.$iVersion) { - let v; - if ("number" === typeof opt.iVersion) v = opt.iVersion; - else if (mod.$xIntegrity) v = 4; - else if (mod.$xShadowName) v = 3; - else if (mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2; - else v = 1; - mod.$iVersion = v; - } - } catch (e) { - if (createdMod) createdMod.dispose(); - throw e; - } - return mod; - }; - /** - Equivalent to calling vtab.setupModule() with this sqlite3_module - object as the call's `this`. - */ - capi.sqlite3_module.prototype.setupModule = function(opt) { - return vtab.setupModule.call(this, opt); - }; - }); - /** - kvvfs - the Key/Value VFS - is an SQLite3 VFS which delegates - storage of its pages and metadata to a key-value store. - - It was conceived in order to support JS's localStorage and - sessionStorage objects. Its native implementation uses files as - key/value storage (one file per record) but the JS implementation - replaces a few methods so that it can use the aforementioned - objects as storage. - - It uses a bespoke ASCII encoding to store each db page as a - separate record and stores some metadata, like the db's unencoded - size and its journal, as individual records. - - kvvfs is significantly less efficient than a plain in-memory db but - it also, as a side effect of its design, offers a JSON-friendly - interchange format for exporting and importing databases. - - kvvfs is _not_ designed for heavy db loads. It is relatively - malloc()-heavy, having to de/allocate frequently, and it - spends much of its time converting the raw db pages into and out of - an ASCII encoding. - - But it _does_ work and is "performant enough" for db work of the - scale of a db which will fit within sessionStorage or localStorage - (just 2-3mb). - - "Version 2" extends it to support using Storage-like objects as - backing storage, Storage being the JS class which localStorage and - sessionStorage both derive from. This essentially moves the backing - store from whatever localStorage and sessionStorage use to an - in-memory object. - - This effort is primarily a stepping stone towards eliminating, if - it proves possible, the POSIX I/O API dependencies in SQLite's WASM - builds. That is: if this VFS works properly, it can be set as the - default VFS and we can eliminate the "unix" VFS from the JS/WASM - builds (as opposed to server-wise/WASI builds). That still, as of - 2025-11-23, a ways away, but it's the main driver for version 2 of - kvvfs. - - Version 2 remains compatible with version 1 databases and always - writes localStorage/sessionStorage metadata in the v1 format, so - such dbs can be manipulated freely by either version. For transient - storage objects (new in version 2), the format of its record keys - is simpified, requiring less space than v1 keys by eliding - redundant (in this context) info from the keys. - - Another benefit of v2 is its ability to export dbs into a - JSON-friendly (but not human-friendly) format. - - A potential, as-yet-unproven, benefit, would be the ability to plug - arbitrary Storage-compatible objects in so that clients could, - e.g. asynchronously post updates to db pages to some back-end for - backups. - */ - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - "use strict"; - const capi = sqlite3.capi, sqlite3_kvvfs_methods = capi.sqlite3_kvvfs_methods, KVVfsFile = capi.KVVfsFile, pKvvfs = sqlite3.capi.sqlite3_vfs_find("kvvfs"); - delete capi.sqlite3_kvvfs_methods; - delete capi.KVVfsFile; - if (!pKvvfs) return; - const util = sqlite3.util, wasm = sqlite3.wasm, toss3 = util.toss3, hop = (o, k) => Object.prototype.hasOwnProperty.call(o, k); - const kvvfsMethods = new sqlite3_kvvfs_methods(wasm.exports.sqlite3__wasm_kvvfs_methods()); - util.assert(32 <= kvvfsMethods.$nKeySize, "unexpected kvvfsMethods.$nKeySize: " + kvvfsMethods.$nKeySize); - /** - Most of the VFS-internal state. - */ - const cache = Object.assign(Object.create(null), { - rxJournalSuffix: /-journal$/, - zKeyJrnl: wasm.allocCString("jrnl"), - zKeySz: wasm.allocCString("sz"), - keySize: kvvfsMethods.$nKeySize, - buffer: Object.assign(Object.create(null), { - n: kvvfsMethods.$nBufferSize, - pool: Object.create(null) - }) - }); - /** - Returns a (cached) wasm.alloc()'d buffer of cache.buffer.n size, - throwing on OOM. - - We leak this one-time alloc because we've no better option. - sqlite3_vfs does not have a finalizer, so we've no place to hook - in the cleanup. We "could" extend sqlite3_shutdown() to have a - cleanup list for stuff like this but that function is never - used in JS, so it's hardly worth it. - */ - cache.memBuffer = (id = 0) => cache.buffer.pool[id] ??= wasm.alloc(cache.buffer.n); - /** Frees the buffer with the given id. */ - cache.memBufferFree = (id) => { - const b = cache.buffer.pool[id]; - if (b) { - wasm.dealloc(b); - delete cache.buffer.pool[id]; - } - }; - const noop = () => {}; - const debug = sqlite3.__isUnderTest ? (...args) => sqlite3.config.debug("kvvfs:", ...args) : noop; - const warn = (...args) => sqlite3.config.warn("kvvfs:", ...args); - const error = (...args) => sqlite3.config.error("kvvfs:", ...args); - /** - Implementation of JS's Storage interface for use as backing store - of the kvvfs. Storage is a native class and its constructor - cannot be legally called from JS, making it impossible to - directly subclass Storage. This class implements (only) the - Storage interface, to make it a drop-in replacement for - localStorage/sessionStorage. (Any behavioral discrepancies are to - be considered bugs.) - - This impl simply proxies a plain, prototype-less Object, suitable - for JSON-ing. - - Design note: Storage has a bit of an odd iteration-related - interface as does not (AFAIK) specify specific behavior regarding - modification during traversal. Because of that, this class does - some seemingly unnecessary things with its #keys member, deleting - and recreating it whenever a property index might be invalidated. - */ - class KVVfsStorage { - #map; - #keys; - #getKeys() { - return this.#keys ??= Object.keys(this.#map); - } - constructor() { - this.clear(); - } - key(n) { - const k = this.#getKeys(); - return n < k.length ? k[n] : null; - } - getItem(k) { - return this.#map[k] ?? null; - } - setItem(k, v) { - if (!hop(this.#map, k)) this.#keys = null; - this.#map[k] = "" + v; - } - removeItem(k) { - if (delete this.#map[k]) this.#keys = null; - } - clear() { - this.#map = Object.create(null); - this.#keys = null; - } - get length() { - return this.#getKeys().length; - } - } - /** True if v is the name of one of the special persistant Storage - objects. */ - const kvvfsIsPersistentName = (v) => "local" === v || "session" === v; - /** - Keys in kvvfs have a prefix of "kvvfs-NAME-", where NAME is the - db name. This key is redundant in JS but it's how kvvfs works (it - saves each key to a separate file, so needs a distinct namespace - per data source name). We retain this prefix in 'local' and - 'session' storage for backwards compatibility and so that they - can co-exist with client data in their storage, but we elide them - from "v2" storage, where they're superfluous. - */ - const kvvfsKeyPrefix = (v) => kvvfsIsPersistentName(v) ? "kvvfs-" + v + "-" : ""; - /** - Throws if storage name n (JS string) is not valid for use as a - storage name. Much of this goes back to kvvfs having a fixed - buffer size for its keys, and the storage name needing to be - encoded in the keys for local/session storage. - - The second argument must only be true when called from xOpen() - - it makes names with a "-journal" suffix legal. - */ - const validateStorageName = function(n, mayBeJournal = false) { - if (kvvfsIsPersistentName(n)) return; - const len = new Blob([n]).size; - if (!len) toss3(capi.SQLITE_MISUSE, "Empty name is not permitted."); - let maxLen = cache.keySize - 1; - if (cache.rxJournalSuffix.test(n)) { - if (!mayBeJournal) toss3(capi.SQLITE_MISUSE, "Storage names may not have a '-journal' suffix."); - } else if (["-wal", "-shm"].filter((v) => n.endsWith(v)).length) toss3(capi.SQLITE_MISUSE, "Storage names may not have a -wal or -shm suffix."); - else maxLen -= 8; - if (len > maxLen) toss3(capi.SQLITE_RANGE, "Storage name is too long. Limit =", maxLen); - let i; - for (i = 0; i < len; ++i) { - const ch = n.codePointAt(i); - if (ch < 32) toss3(capi.SQLITE_RANGE, "Illegal character (" + ch + "d) in storage name:", n); - } - }; - /** - Create a new instance of the objects which go into - cache.storagePool, with a refcount of 1. If passed a Storage-like - object as its second argument, it is used for the storage, - otherwise it creates a new KVVfsStorage object. - */ - const newStorageObj = (name, storage = void 0) => Object.assign(Object.create(null), { - jzClass: name, - refc: 1, - deleteAtRefc0: false, - storage: storage || new KVVfsStorage(), - keyPrefix: kvvfsKeyPrefix(name), - files: [], - listeners: void 0 - }); - /** - Public interface for kvvfs v2. The capi.sqlite3_js_kvvfs_...() - routines remain in place for v1. Some members of this class proxy - to those functions but use different default argument values in - some cases. - */ - const kvvfs = sqlite3.kvvfs = Object.create(null); - if (sqlite3.__isUnderTest) kvvfs.log = Object.assign(Object.create(null), { - xOpen: false, - xClose: false, - xWrite: false, - xRead: false, - xSync: false, - xAccess: false, - xFileControl: false, - xRcrdRead: false, - xRcrdWrite: false, - xRcrdDelete: false - }); - /** - Deletes the cache.storagePool entries for store (a - cache.storagePool entry) and its db/journal counterpart. - */ - const deleteStorage = function(store) { - const other = cache.rxJournalSuffix.test(store.jzClass) ? store.jzClass.replace(cache.rxJournalSuffix, "") : store.jzClass + "-journal"; - kvvfs?.log?.xClose && debug("cleaning up storage handles [", store.jzClass, other, "]", store); - delete cache.storagePool[store.jzClass]; - delete cache.storagePool[other]; - if (!sqlite3.__isUnderTest) { - delete store.storage; - delete store.refc; - } - }; - /** - Add both store.jzClass and store.jzClass+"-journal" - to cache,storagePool. - */ - const installStorageAndJournal = (store) => cache.storagePool[store.jzClass] = cache.storagePool[store.jzClass + "-journal"] = store; - /** - The public name of the current thread's transient storage - object. A storage object with this name gets preinstalled. - */ - const nameOfThisThreadStorage = "."; - /** - Map of JS-stringified KVVfsFile::zClass names to - reference-counted Storage objects. These objects are created in - xOpen(). Their refcount is decremented in xClose(), and the - record is destroyed if the refcount reaches 0. We refcount so - that concurrent active xOpen()s on a given name, and within a - given thread, use the same storage object. - */ - cache.storagePool = Object.assign(Object.create(null), { [nameOfThisThreadStorage]: newStorageObj(nameOfThisThreadStorage) }); - if (globalThis.Storage) { - if (globalThis.localStorage instanceof globalThis.Storage) cache.storagePool.local = newStorageObj("local", globalThis.localStorage); - if (globalThis.sessionStorage instanceof globalThis.Storage) cache.storagePool.session = newStorageObj("session", globalThis.sessionStorage); - } - cache.builtinStorageNames = Object.keys(cache.storagePool); - const isBuiltinName = (n) => cache.builtinStorageNames.indexOf(n) > -1; - for (const k of Object.keys(cache.storagePool)) { - const orig = cache.storagePool[k]; - cache.storagePool[k + "-journal"] = orig; - } - cache.setError = (e = void 0, dfltErrCode = capi.SQLITE_ERROR) => { - if (e) { - cache.lastError = e; - return e.resultCode | 0 || dfltErrCode; - } - delete cache.lastError; - return 0; - }; - cache.popError = () => { - const e = cache.lastError; - delete cache.lastError; - return e; - }; - /** Exception handler for notifyListeners(). */ - const catchForNotify = (e) => { - warn("kvvfs.listener handler threw:", e); - }; - const kvvfsDecode = wasm.exports.sqlite3__wasm_kvvfs_decode; - const kvvfsEncode = wasm.exports.sqlite3__wasm_kvvfs_encode; - /** - Listener events and their argument(s) (via the callback(ev) - ev.data member): - - 'open': number of opened handles on this storage. - - 'close': number of opened handles on this storage. - - 'write': key, value - - 'delete': key - - 'sync': true if it's from xSync(), false if it's from - xFileControl(). - - For efficiency's sake, all calls to this function should - be in the form: - - store.listeners && notifyListeners(...); - - Failing to do so will trigger an exceptin in this function (which - will be ignored but may produce a console warning). - */ - const notifyListeners = async function(eventName, store, ...args) { - try { - if (store.keyPrefix && args[0]) args[0] = args[0].replace(store.keyPrefix, ""); - let u8enc, z0, z1, wcache; - for (const ear of store.listeners) { - const ev = Object.create(null); - ev.storageName = store.jzClass; - ev.type = eventName; - ear.decodePages; - const f = ear.events[eventName]; - if (f) { - if (!ear.includeJournal && args[0] === "jrnl") continue; - if ("write" === eventName && ear.decodePages && +args[0] > 0) { - ev.data = [args[0]]; - if (wcache?.[args[0]]) { - ev.data[1] = wcache[args[0]]; - continue; - } - u8enc ??= new TextEncoder("utf-8"); - z0 ??= cache.memBuffer(10); - z1 ??= cache.memBuffer(11); - const u = u8enc.encode(args[1]); - const heap = wasm.heap8u(); - heap.set(u, Number(z0)); - heap[wasm.ptr.addn(z0, u.length)] = 0; - const rc = kvvfsDecode(z0, z1, cache.buffer.n); - if (rc > 0) { - wcache ??= Object.create(null); - wcache[args[0]] = ev.data[1] = heap.slice(Number(z1), wasm.ptr.addn(z1, rc)); - } else continue; - } else ev.data = args.length ? args.length === 1 ? args[0] : args : void 0; - try { - f(ev)?.catch?.(catchForNotify); - } catch (e) { - warn("notifyListeners [", store.jzClass, "]", eventName, e); - } - } - } - } catch (e) { - catchForNotify(e); - } - }; - /** - Returns the storage object mapped to the given string zClass - (C-string pointer or JS string). - */ - const storageForZClass = (zClass) => "string" === typeof zClass ? cache.storagePool[zClass] : cache.storagePool[wasm.cstrToJs(zClass)]; - const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKey; - /** - Returns a C string from kvvfsMakeKey() OR returns zKey. In the - former case the memory is static, so must be copied before a - second call. zKey MUST be a pointer passed to a VFS/file method, - to allow us to avoid an alloc and/or an snprintf(). It requires - C-string arguments for zClass and zKey. zClass may be NULL but - zKey may not. - */ - const zKeyForStorage = (store, zClass, zKey) => { - return zClass && store.keyPrefix ? kvvfsMakeKey(zClass, zKey) : zKey; - }; - const jsKeyForStorage = (store, zClass, zKey) => wasm.cstrToJs(zKeyForStorage(store, zClass, zKey)); - const storageGetDbSize = (store) => +store.storage.getItem(store.keyPrefix + "sz"); - /** - sqlite3_file pointers => objects, each of which has: - - .file = KVVfsFile instance - - .jzClass = JS-string form of f.$zClass - - .storage = Storage object. It is shared between a db and its - journal. - */ - const pFileHandles = /* @__PURE__ */ new Map(); - /** - Original WASM functions for methods we partially override. - */ - const originalMethods = { - vfs: Object.create(null), - ioDb: Object.create(null), - ioJrnl: Object.create(null) - }; - const pVfs = new capi.sqlite3_vfs(kvvfsMethods.$pVfs); - const pIoDb = new capi.sqlite3_io_methods(kvvfsMethods.$pIoDb); - const pIoJrnl = new capi.sqlite3_io_methods(kvvfsMethods.$pIoJrnl); - const recordHandler = Object.create(null); - const kvvfsInternal = Object.assign(Object.create(null), { - pFileHandles, - cache, - storageForZClass, - KVVfsStorage, - disablePageSizeChange: true - }); - if (kvvfs.log) kvvfs.internal = kvvfsInternal; - /** - Implementations for members of the object referred to by - sqlite3__wasm_kvvfs_methods(). We swap out some native - implementations with these so that we can use JS Storage for - their backing store. - */ - const methodOverrides = { - recordHandler: { - xRcrdRead: (zClass, zKey, zBuf, nBuf) => { - try { - const jzClass = wasm.cstrToJs(zClass); - const store = storageForZClass(jzClass); - if (!store) return -1; - const jXKey = jsKeyForStorage(store, zClass, zKey); - kvvfs?.log?.xRcrdRead && warn("xRcrdRead", jzClass, jXKey, nBuf, store); - const jV = store.storage.getItem(jXKey); - if (null === jV) return -1; - const nV = jV.length; - if (nBuf <= 0) return nV; - else if (1 === nBuf) { - wasm.poke(zBuf, 0); - return nV; - } - if (nBuf + 1 < nV) toss3(capi.SQLITE_RANGE, "xRcrdRead()", jzClass, jXKey, "input buffer is too small: need", nV, "but have", nBuf); - const zV = cache.memBuffer(0); - const heap = wasm.heap8(); - let i; - for (i = 0; i < nV; ++i) heap[wasm.ptr.add(zV, i)] = jV.codePointAt(i) & 255; - heap.copyWithin(Number(zBuf), Number(zV), wasm.ptr.addn(zV, i)); - heap[wasm.ptr.add(zBuf, nV)] = 0; - return nBuf; - } catch (e) { - error("kvrecordRead()", e); - cache.setError(e); - return -2; - } - }, - xRcrdWrite: (zClass, zKey, zData) => { - try { - const store = storageForZClass(zClass); - const jxKey = jsKeyForStorage(store, zClass, zKey); - const jData = wasm.cstrToJs(zData); - kvvfs?.log?.xRcrdWrite && warn("xRcrdWrite", jxKey, store); - store.storage.setItem(jxKey, jData); - store.listeners && notifyListeners("write", store, jxKey, jData); - return 0; - } catch (e) { - error("kvrecordWrite()", e); - return cache.setError(e, capi.SQLITE_IOERR); - } - }, - xRcrdDelete: (zClass, zKey) => { - try { - const store = storageForZClass(zClass); - const jxKey = jsKeyForStorage(store, zClass, zKey); - kvvfs?.log?.xRcrdDelete && warn("xRcrdDelete", jxKey, store); - store.storage.removeItem(jxKey); - store.listeners && notifyListeners("delete", store, jxKey); - return 0; - } catch (e) { - error("kvrecordDelete()", e); - return cache.setError(e, capi.SQLITE_IOERR); - } - } - }, - vfs: { - xOpen: function(pProtoVfs, zName, pProtoFile, flags, pOutFlags) { - cache.popError(); - let zToFree; - try { - if (!zName) { - zToFree = wasm.allocCString("" + pProtoFile + "." + (Math.random() * 1e5 | 0)); - zName = zToFree; - } - const jzClass = wasm.cstrToJs(zName); - kvvfs?.log?.xOpen && debug("xOpen", jzClass, "flags =", flags); - validateStorageName(jzClass, true); - if (flags & (capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_TEMP_DB | capi.SQLITE_OPEN_TRANSIENT_DB) && cache.rxJournalSuffix.test(jzClass)) toss3(capi.SQLITE_ERROR, "DB files may not have a '-journal' suffix."); - let s = storageForZClass(jzClass); - if (!s && !(flags & capi.SQLITE_OPEN_CREATE)) toss3(capi.SQLITE_ERROR, "Storage not found:", jzClass); - const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile, flags, pOutFlags); - if (rc) return rc; - let deleteAt0 = !!(capi.SQLITE_OPEN_DELETEONCLOSE & flags); - if (wasm.isPtr(arguments[1])) { - if (capi.sqlite3_uri_boolean(zName, "delete-on-close", 0)) deleteAt0 = true; - } - const f = new KVVfsFile(pProtoFile); - util.assert(f.$zClass, "Missing f.$zClass"); - f.addOnDispose(zToFree); - zToFree = void 0; - if (s) { - ++s.refc; - s.files.push(f); - wasm.poke32(pOutFlags, flags); - } else { - wasm.poke32(pOutFlags, flags | capi.SQLITE_OPEN_CREATE); - util.assert(!f.$isJournal, "Opening a journal before its db? " + jzClass); - const nm = jzClass.replace(cache.rxJournalSuffix, ""); - s = newStorageObj(nm); - installStorageAndJournal(s); - s.files.push(f); - s.deleteAtRefc0 = deleteAt0; - kvvfs?.log?.xOpen && debug("xOpen installed storage handle [", nm, nm + "-journal", "]", s); - } - pFileHandles.set(pProtoFile, { - store: s, - file: f, - jzClass - }); - s.listeners && notifyListeners("open", s, s.files.length); - return 0; - } catch (e) { - warn("xOpen:", e); - return cache.setError(e); - } finally { - zToFree && wasm.dealloc(zToFree); - } - }, - xDelete: function(pVfs, zName, iSyncFlag) { - cache.popError(); - try { - const jzName = wasm.cstrToJs(zName); - if (cache.rxJournalSuffix.test(jzName)) recordHandler.xRcrdDelete(zName, cache.zKeyJrnl); - return 0; - } catch (e) { - warn("xDelete", e); - return cache.setError(e); - } - }, - xAccess: function(pProtoVfs, zPath, flags, pResOut) { - cache.popError(); - try { - const s = storageForZClass(zPath); - const jzPath = s?.jzClass || wasm.cstrToJs(zPath); - if (kvvfs?.log?.xAccess) debug("xAccess", jzPath, "flags =", flags, "*pResOut =", wasm.peek32(pResOut), "store =", s); - if (!s) - /** The xAccess method returns [SQLITE_OK] on success or some - ** non-zero error code if there is an I/O error or if the name of - ** the file given in the second argument is illegal. - */ - try { - validateStorageName(jzPath); - } catch (e) { - wasm.poke32(pResOut, 0); - return 0; - } - if (s) { - const key = s.keyPrefix + (cache.rxJournalSuffix.test(jzPath) ? "jrnl" : "1"); - const res = s.storage.getItem(key) ? 0 : 1; - wasm.poke32(pResOut, res); - } else wasm.poke32(pResOut, 0); - return 0; - } catch (e) { - error("xAccess", e); - return cache.setError(e); - } - }, - xRandomness: function(pVfs, nOut, pOut) { - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; - return nOut; - }, - xGetLastError: function(pVfs, nOut, pOut) { - const e = cache.popError(); - debug("xGetLastError", e); - if (e) { - const scope = wasm.scopedAllocPush(); - try { - const [cMsg, n] = wasm.scopedAllocCString(e.message, true); - wasm.cstrncpy(pOut, cMsg, nOut); - if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); - debug("set xGetLastError", e.message); - return e.resultCode | 0 || capi.SQLITE_IOERR; - } catch (e) { - return capi.SQLITE_NOMEM; - } finally { - wasm.scopedAllocPop(scope); - } - } - return 0; - } - }, - ioDb: { - xClose: function(pFile) { - cache.popError(); - try { - const h = pFileHandles.get(pFile); - kvvfs?.log?.xClose && debug("xClose", pFile, h); - if (h) { - pFileHandles.delete(pFile); - const s = h.store; - s.files = s.files.filter((v) => v !== h.file); - if (--s.refc <= 0 && s.deleteAtRefc0) deleteStorage(s); - originalMethods.ioDb.xClose(pFile); - h.file.dispose(); - s.listeners && notifyListeners("close", s, s.files.length); - } - return 0; - } catch (e) { - error("xClose", e); - return cache.setError(e); - } - }, - xFileControl: function(pFile, opId, pArg) { - cache.popError(); - try { - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - kvvfs?.log?.xFileControl && debug("xFileControl", h, "op =", opId); - if (opId === capi.SQLITE_FCNTL_PRAGMA && kvvfsInternal.disablePageSizeChange) { - const zName = wasm.peekPtr(wasm.ptr.add(pArg, wasm.ptr.size)); - if ("page_size" === wasm.cstrToJs(zName)) { - kvvfs?.log?.xFileControl && debug("xFileControl pragma", wasm.cstrToJs(zName)); - const zVal = wasm.peekPtr(wasm.ptr.add(pArg, 2 * wasm.ptr.size)); - if (zVal) { - kvvfs?.log?.xFileControl && warn("xFileControl pragma", h, "NOT setting page size to", wasm.cstrToJs(zVal)); - h.file.$szPage = -1; - return 0; - } else if (h.file.$szPage > 0) { - kvvfs?.log?.xFileControl && warn("xFileControl", h, "getting page size", h.file.$szPage); - wasm.pokePtr(pArg, wasm.allocCString("" + h.file.$szPage)); - return 0; - } - } - } - const rc = originalMethods.ioDb.xFileControl(pFile, opId, pArg); - if (0 == rc && capi.SQLITE_FCNTL_SYNC === opId) h.store.listeners && notifyListeners("sync", h.store, false); - return rc; - } catch (e) { - error("xFileControl", e); - return cache.setError(e); - } - }, - xSync: function(pFile, flags) { - cache.popError(); - try { - const h = pFileHandles.get(pFile); - kvvfs?.log?.xSync && debug("xSync", h); - util.assert(h, "Missing KVVfsFile handle"); - const rc = originalMethods.ioDb.xSync(pFile, flags); - if (0 == rc && h.store.listeners) notifyListeners("sync", h.store, true); - return rc; - } catch (e) { - error("xSync", e); - return cache.setError(e); - } - }, - xRead: function(pFile, pTgt, n, iOff64) { - cache.popError(); - try { - if (kvvfs?.log?.xRead) { - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xRead", n, iOff64, h); - } - return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); - } catch (e) { - error("xRead", e); - return cache.setError(e); - } - }, - xWrite: function(pFile, pSrc, n, iOff64) { - cache.popError(); - try { - if (kvvfs?.log?.xWrite) { - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xWrite", n, iOff64, h); - } - return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); - } catch (e) { - error("xWrite", e); - return cache.setError(e); - } - } - }, - ioJrnl: { xClose: true } - }; - debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, kvvfsMethods, capi.sqlite3_file.structInfo, KVVfsFile.structInfo); - try { - util.assert(cache.buffer.n > 1024 * 129, "Heap buffer is not large enough"); - for (const e of Object.entries(methodOverrides.recordHandler)) { - const k = e[0], f = e[1]; - recordHandler[k] = f; - kvvfsMethods[kvvfsMethods.memberKey(k)] = wasm.installFunction(kvvfsMethods.memberSignature(k), f); - } - for (const e of Object.entries(methodOverrides.vfs)) { - const k = e[0], f = e[1], km = pVfs.memberKey(k), member = pVfs.structInfo.members[k] || util.toss("Missing pVfs.structInfo[", k, "]"); - originalMethods.vfs[k] = wasm.functionEntry(pVfs[km]); - pVfs[km] = wasm.installFunction(member.signature, f); - } - for (const e of Object.entries(methodOverrides.ioDb)) { - const k = e[0], f = e[1], km = pIoDb.memberKey(k); - originalMethods.ioDb[k] = wasm.functionEntry(pIoDb[km]) || util.toss("Missing native pIoDb[", km, "]"); - pIoDb[km] = wasm.installFunction(pIoDb.memberSignature(k), f); - } - for (const e of Object.entries(methodOverrides.ioJrnl)) { - const k = e[0], f = e[1], km = pIoJrnl.memberKey(k); - originalMethods.ioJrnl[k] = wasm.functionEntry(pIoJrnl[km]) || util.toss("Missing native pIoJrnl[", km, "]"); - if (true === f) pIoJrnl[km] = pIoDb[km] || util.toss("Missing copied pIoDb[", km, "]"); - else pIoJrnl[km] = wasm.installFunction(pIoJrnl.memberSignature(k), f); - } - } finally { - kvvfsMethods.dispose(); - pVfs.dispose(); - pIoDb.dispose(); - pIoJrnl.dispose(); - } - /** - Clears all storage used by the kvvfs DB backend, deleting any - DB(s) stored there. - - Its argument must be the name of a kvvfs storage object: - - - 'session' - - 'local' - - '' - see below. - - A transient kvvfs storage object name. - - In the first two cases, only sessionStorage resp. localStorage is - cleared. An empty string resolves to both 'local' and 'session' - storage. - - Returns the number of entries cleared. - - As of kvvfs version 2: - - This API is available in Worker threads but does not have access - to localStorage or sessionStorage in them. Prior versions did not - include this API in Worker threads. - - Differences in this function in version 2: - - - It accepts an arbitrary storage name. In v1 this was a silent - no-op for any names other than ('local','session',''). - - - It throws if a db currently has the storage opened UNLESS the - storage object is localStorage or sessionStorage. That version 1 - did not throw for this case was due to an architectural - limitation which has since been overcome, but removal of - JsStorageDb.prototype.clearStorage() would be a backwards compatibility - break, so this function permits wiping the storage for those two - cases even if they are opened. Use with case. - */ - const sqlite3_js_kvvfs_clear = function callee(which) { - if ("" === which) return callee("local") + callee("session"); - const store = storageForZClass(which); - if (!store) return 0; - if (store.files.length) if (globalThis.localStorage === store.storage || globalThis.sessionStorage === store.storage) {} else toss3(capi.SQLITE_ACCESS, "Cannot clear in-use database storage."); - const s = store.storage; - const toRm = []; - let i, n = s.length; - for (i = 0; i < n; ++i) { - const k = s.key(i); - if (!store.keyPrefix || k.startsWith(store.keyPrefix)) toRm.push(k); - } - toRm.forEach((kk) => s.removeItem(kk)); - return toRm.length; - }; - /** - This routine estimates the approximate amount of - storage used by the given kvvfs back-end. - - Its arguments are as documented for sqlite3_js_kvvfs_clear(), - only the operation this performs is different. - - The returned value is twice the "length" value of every matching - key and value, noting that JavaScript stores each character in 2 - bytes. - - The returned size is not authoritative from the perspective of - how much data can fit into localStorage and sessionStorage, as - the precise algorithms for determining those limits are - unspecified and may include per-entry overhead invisible to - clients. - */ - const sqlite3_js_kvvfs_size = function callee(which) { - if ("" === which) return callee("local") + callee("session"); - const store = storageForZClass(which); - if (!store) return 0; - const s = store.storage; - let i, sz = 0; - for (i = 0; i < s.length; ++i) { - const k = s.key(i); - if (!store.keyPrefix || k.startsWith(store.keyPrefix)) { - sz += k.length; - sz += s.getItem(k).length; - } - } - return sz * 2; - }; - /** - Exports a kvvfs storage object to an object, optionally - JSON-friendly. - - Usages: - - thisfunc(storageName); - thisfunc(options); - - In the latter case, the options object must be an object with - the following properties: - - - "name" (string) required. The storage to export. - - - "decodePages" (bool=false). If true, the .pages result property - holdes Uint8Array objects holding the raw binary-format db - pages. The default is to use kvvfs-encoded string pages - (JSON-friendly). - - - "includeJournal" (bool=false). If true and the db has a current - journal, it is exported as well. (Kvvfs journals are stored as a - single record within the db's storage object.) - - The returned object is structured as follows... - - - "name": the name of the storage. This is 'local' or 'session' - for localStorage resp. sessionStorage, and an arbitrary name for - transient storage. This propery may be changed before passing - this object to sqlite3_js_kvvfs_import() in order to - import into a different storage object. - - - "timestamp": the time this function was called, in Unix - epoch milliseconds. - - - "size": the unencoded db size. - - - "journal": if options.includeJournal is true and this db has a - journal, it is stored as a string here, otherwise this property - is not set. - - - "pages": An array holding the raw encoded db pages in their - proper order. - - Throws if this db is not opened. - - The encoding of the underlying database is not part of this - interface - it is simply passed on as-is. Interested parties are - directed to src/os_kv.c in the SQLite source tree, with the - caveat that that code also does not offer a public interface. - i.e. the encoding is a private implementation detail of kvvfs. - The format may be changed in the future but kvvfs will continue - to support the current form. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_export = function callee(...args) { - let opt; - if (1 === args.length && "object" === typeof args[0]) opt = args[0]; - else if (args.length) opt = Object.assign(Object.create(null), { name: args[0] }); - const store = opt ? storageForZClass(opt.name) : null; - if (!store) toss3(capi.SQLITE_NOTFOUND, "There is no kvvfs storage named", opt?.name); - const s = store.storage; - const rc = Object.assign(Object.create(null), { - name: store.jzClass, - timestamp: Date.now(), - pages: [] - }); - const pages = Object.create(null); - const keyPrefix = store.keyPrefix; - const rxTail = keyPrefix ? /^kvvfs-[^-]+-(\w+)/ : void 0; - let i = 0, n = s.length; - for (; i < n; ++i) { - const k = s.key(i); - if (!keyPrefix || k.startsWith(keyPrefix)) { - let kk = (keyPrefix ? rxTail.exec(k) : void 0)?.[1] ?? k; - switch (kk) { - case "jrnl": - if (opt.includeJournal) rc.journal = s.getItem(k); - break; - case "sz": - rc.size = +s.getItem(k); - break; - default: - kk = +kk; - if (!util.isInt32(kk) || kk <= 0) toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: " + k); - if (opt.decodePages) { - const spg = s.getItem(k), n = spg.length, z = cache.memBuffer(0), zDec = cache.memBuffer(1), heap = wasm.heap8u(); - let i = 0; - for (; i < n; ++i) heap[wasm.ptr.add(z, i)] = spg.codePointAt(i) & 255; - heap[wasm.ptr.add(z, i)] = 0; - const nDec = kvvfsDecode(z, zDec, cache.buffer.n); - pages[kk] = heap.slice(Number(zDec), wasm.ptr.addn(zDec, nDec)); - } else pages[kk] = s.getItem(k); - break; - } - } - } - if (opt.decodePages) cache.memBufferFree(1); - Object.keys(pages).map((v) => +v).sort().forEach((v) => rc.pages.push(pages[v])); - return rc; - }; - /** - The counterpart of sqlite3_js_kvvfs_export(). Its - argument must be the result of that function() or - a compatible one. - - This either replaces the contents of an existing transient - storage object or installs one named exp.name, setting - the storage's db contents to that of the exp object. - - Throws on error. Error conditions include: - - - The given storage object is currently opened by any db. - Performing this page-by-page import would invoke undefined - behavior on them. - - - Malformed input object. - - If it throws after starting the import then it clears the storage - before returning, to avoid leaving the db in an undefined - state. It may throw for any of the above-listed conditions before - reaching that step, in which case the db is not modified. If - exp.name refers to a new storage name then if it throws, the name - does not get installed. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_import = function(exp, overwrite = false) { - if (!exp?.timestamp || !exp.name || void 0 === exp.size || !Array.isArray(exp.pages)) toss3(capi.SQLITE_MISUSE, "Malformed export object."); - else if (!exp.size || exp.size !== (exp.size | 0) || exp.size >= 2147483647) toss3(capi.SQLITE_RANGE, "Invalid db size: " + exp.size); - validateStorageName(exp.name); - let store = storageForZClass(exp.name); - const isNew = !store; - if (store) { - if (!overwrite) toss3(capi.SQLITE_ACCESS, "Storage '" + exp.name + "' already exists and", "overwrite was not specified."); - else if (!store.files || !store.jzClass) toss3(capi.SQLITE_ERROR, "Internal storage object", exp.name, "seems to be malformed."); - else if (store.files.length) toss3(capi.SQLITE_IOERR_ACCESS, "Cannot import db storage while it is in use."); - sqlite3_js_kvvfs_clear(exp.name); - } else store = newStorageObj(exp.name); - const keyPrefix = kvvfsKeyPrefix(exp.name); - let zEnc; - try { - const s = store.storage; - s.setItem(keyPrefix + "sz", exp.size); - if (exp.journal) s.setItem(keyPrefix + "jrnl", exp.journal); - if (exp.pages[0] instanceof Uint8Array) exp.pages.forEach((u, ndx) => { - const n = u.length; - zEnc ??= cache.memBuffer(1); - const zBin = cache.memBuffer(0), heap = wasm.heap8u(); - heap.set(u, Number(zBin)); - heap[wasm.ptr.addn(zBin, n)] = 0; - const rc = kvvfsEncode(zBin, n, zEnc); - util.assert(rc < cache.buffer.n, "Impossibly long output - possibly smashed the heap"); - util.assert(0 === wasm.peek8(wasm.ptr.add(zEnc, rc)), "Expecting NUL-terminated encoded output"); - const jenc = wasm.cstrToJs(zEnc); - s.setItem(keyPrefix + (ndx + 1), jenc); - }); - else if (exp.pages[0]) exp.pages.forEach((v, ndx) => s.setItem(keyPrefix + (ndx + 1), v)); - if (isNew) installStorageAndJournal(store); - } catch { - if (!isNew) try { - sqlite3_js_kvvfs_clear(exp.name); - } catch (ee) {} - } finally { - if (zEnc) cache.memBufferFree(1); - } - return this; - }; - /** - If no kvvfs storage exists with the given name, one is - installed. If one exists, its reference count is increased so - that it won't be freed by the closing of a database or journal - file. - - Throws if the name is not valid for a new storage object. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_reserve = function(name) { - let store = storageForZClass(name); - if (store) { - ++store.refc; - return; - } - validateStorageName(name); - installStorageAndJournal(newStorageObj(name)); - }; - /** - Conditionally "unlinks" a kvvfs storage object, reducing its - reference count by 1. - - This is a no-op if name ends in "-journal" or refers to a - built-in storage object. - - It will not lower the refcount below the number of - currently-opened db/journal files for the storage (so that it - cannot delete it out from under them). - - If the refcount reaches 0 then the storage object is - removed. - - Returns true if it reduces the refcount, else false. A result of - true does not necessarily mean that the storage unit was removed, - just that its refcount was lowered. Similarly, a result of false - does not mean that the storage is removed - it may still have - opened handles. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_unlink = function(name) { - const store = storageForZClass(name); - if (!store || kvvfsIsPersistentName(store.jzClass) || isBuiltinName(store.jzClass) || cache.rxJournalSuffix.test(name)) return false; - if (store.refc > store.files.length || 0 === store.files.length) { - if (--store.refc <= 0) deleteStorage(store); - return true; - } - return false; - }; - /** - Adds an event listener to a kvvfs storage object. The idea is - that this can be used to asynchronously back up one kvvfs storage - object to another or another channel entirely. (The caveat in the - latter case is that kvvfs's format is not readily consumable by - downstream code.) - - Its argument must be an object with the following properties: - - - storage: the name of the kvvfs storage object. - - - reserve [=false]: if true, sqlite3_js_kvvfs_reserve() is used - to ensure that the storage exists if it does not already. - If this is false and the storage does not exist then an - exception is thrown. - - - events: an object which may have any of the following - callback function properties: open, close, write, delete. - - - decodePages [=false]: if true, write events will receive each - db page write in the form of a Uint8Array holding the raw binary - db page. The default is to emit the kvvfs-format page because it - requires no extra work, we already have it in hand, and it's - often smaller. It's not great for interchange, though. - - - includeJournal [=false]: if true, writes and deletes of - "jrnl" records are included. If false, no events are sent - for journal updates. - - Passing the same object to sqlite3_js_kvvfs_unlisten() will - remove the listener. - - Each one of the events callbacks will be called asynchronously - when the given storage performs those operations. They may be - asynchronous functions but are not required to be (the events are - fired async either way, but making the event callbacks async may - be advantageous when multiple listeners are involved). All - exceptions, including those via Promises, are ignored but may (or - may not) trigger warning output on the console. - - Each callback gets passed a single object with the following - properties: - - .type = the same as the name of the callback - - .storageName = the name of the storage object - - .data = callback-dependent: - - - 'open' and 'close' get an integer, the number of - currently-opened handles on the storage. - - - 'write' gets a length-two array holding the key and value which - were written. The key is always a string, even if it's a db page - number. For db-page records, the value's type depends on - opt.decodePages. All others, including the journal, are strings. - (The journal, being a kvvfs-specific format, is delivered in - that same JSON-friendly format.) More details below. - - - 'delete' gets the string-type key of the deleted record. - - - 'sync' gets a boolean value: true if it was triggered by db - file's xSync(), false if it was triggered by xFileControl(). The - latter triggers before the xSync() and also triggers if the DB - has PRAGMA SYNCHRONOUS=OFF (in which case xSync() is not - triggered). - - The key/value arguments to 'write', and key argument to 'delete', - are in one of the following forms: - - - 'sz' = the unencoded db size as a string. This specific key is - key is never deleted, so is only ever passed to 'write' events. - - - 'jrnl' = the current db journal as a kvvfs-encoded string. This - journal format is not useful anywhere except in the kvvfs - internals. These events are not fired if opt.includeJournal is - false. - - - '[1-9][0-9]*' (a db page number) = Its type depends on - opt.decodePages. These may be written and deleted in arbitrary - order. - - Design note: JS has StorageEvents but only in the main thread, - which is why the listeners are not based on that. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_listen = function(opt) { - if (!opt || "object" !== typeof opt) toss3(capi.SQLITE_MISUSE, "Expecting a listener object."); - let store = storageForZClass(opt.storage); - if (!store) if (opt.storage && opt.reserve) { - sqlite3_js_kvvfs_reserve(opt.storage); - store = storageForZClass(opt.storage); - util.assert(store, "Unexpectedly cannot fetch reserved storage " + opt.storage); - } else toss3(capi.SQLITE_NOTFOUND, "No such storage:", opt.storage); - if (opt.events) (store.listeners ??= []).push(opt); - }; - /** - Removes the kvvfs event listeners for the given options - object. It must be passed the same object instance which was - passed to sqlite3_js_kvvfs_listen(). - - This has no side effects if opt is invalid or is not a match for - any listeners. - - Return true if it unregisters its argument, else false. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_unlisten = function(opt) { - const store = storageForZClass(opt?.storage); - if (store?.listeners && opt.events) { - const n = store.listeners.length; - store.listeners = store.listeners.filter((v) => v !== opt); - const rc = n > store.listeners.length; - if (!store.listeners.length) store.listeners = void 0; - return rc; - } - return false; - }; - sqlite3.kvvfs.reserve = sqlite3_js_kvvfs_reserve; - sqlite3.kvvfs.import = sqlite3_js_kvvfs_import; - sqlite3.kvvfs.export = sqlite3_js_kvvfs_export; - sqlite3.kvvfs.unlink = sqlite3_js_kvvfs_unlink; - sqlite3.kvvfs.listen = sqlite3_js_kvvfs_listen; - sqlite3.kvvfs.unlisten = sqlite3_js_kvvfs_unlisten; - sqlite3.kvvfs.exists = (name) => !!storageForZClass(name); - sqlite3.kvvfs.estimateSize = sqlite3_js_kvvfs_size; - sqlite3.kvvfs.clear = sqlite3_js_kvvfs_clear; - if (globalThis.Storage) { - /** - Prior to version 2, kvvfs was only available in the main - thread. We retain that for the v1 APIs, exposing them only in - the main UI thread. As of version 2, kvvfs is available in all - threads but only via its v2 interface (sqlite3.kvvfs). - - These versions have a default argument value of "" which the v2 - versions lack. - */ - capi.sqlite3_js_kvvfs_size = (which = "") => sqlite3_js_kvvfs_size(which); - capi.sqlite3_js_kvvfs_clear = (which = "") => sqlite3_js_kvvfs_clear(which); - } - if (sqlite3.oo1?.DB) { - /** - Functionally equivalent to DB(storageName,'c','kvvfs') except - that it throws if the given storage name is not one of 'local' - or 'session'. - - As of version 3.46, the argument may optionally be an options - object in the form: - - { - filename: 'session'|'local', - ... etc. (all options supported by the DB ctor) - } - - noting that the 'vfs' option supported by main DB - constructor is ignored here: the vfs is always 'kvvfs'. - */ - const DB = sqlite3.oo1.DB; - sqlite3.oo1.JsStorageDb = function(storageName = sqlite3.oo1.JsStorageDb.defaultStorageName) { - const opt = DB.dbCtorHelper.normalizeArgs(...arguments); - opt.vfs = "kvvfs"; - switch (opt.filename) { - case ":sessionStorage:": - opt.filename = "session"; - break; - case ":localStorage:": - opt.filename = "local"; - break; - } - const m = /(file:(\/\/)?)([^?]+)/.exec(opt.filename); - validateStorageName(m ? m[3] : opt.filename); - DB.dbCtorHelper.call(this, opt); - }; - sqlite3.oo1.JsStorageDb.defaultStorageName = cache.storagePool.session ? "session" : nameOfThisThreadStorage; - const jdb = sqlite3.oo1.JsStorageDb; - jdb.prototype = Object.create(DB.prototype); - jdb.clearStorage = sqlite3_js_kvvfs_clear; - /** - DEPRECATED: the inherited method of this name (as opposed to - the "static" class method) is deprecated with version 2 of - kvvfs. This function will, for backwards comaptibility, - continue to work with localStorage and sessionStorage, but will - throw for all other storage because they are opened. Version 1 - was not capable of recognizing that the storage was opened so - permitted wiping it out at any time, but that was arguably a - bug. - - Clears this database instance's storage or throws if this - instance has been closed. Returns the number of - database pages which were cleaned up. - */ - jdb.prototype.clearStorage = function() { - return jdb.clearStorage(this.affirmOpen().dbFilename(), true); - }; - /** Equivalent to sqlite3_js_kvvfs_size(). */ - jdb.storageSize = sqlite3_js_kvvfs_size; - /** - Returns the _approximate_ number of bytes this database takes - up in its storage or throws if this instance has been closed. - */ - jdb.prototype.storageSize = function() { - return jdb.storageSize(this.affirmOpen().dbFilename(), true); - }; - } - if (sqlite3.__isUnderTest && sqlite3.vtab) { - /** - An eponymous vtab for inspecting the kvvfs state. This is only - intended for use in testing and development, not part of the - public API. - */ - const cols = Object.assign(Object.create(null), { - rowid: { type: "INTEGER" }, - name: { type: "TEXT" }, - nRef: { type: "INTEGER" }, - nOpen: { type: "INTEGER" }, - isTransient: { type: "INTEGER" }, - dbSize: { type: "INTEGER" } - }); - Object.keys(cols).forEach((v, i) => cols[v].colId = i); - const VT = sqlite3.vtab; - const ProtoCursor = Object.assign(Object.create(null), { row: function() { - return cache.storagePool[this.names[this.rowid]]; - } }); - Object.assign(Object.create(ProtoCursor), { - rowid: 0, - names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) - }); - const cursorState = function(cursor, reset) { - const o = cursor instanceof capi.sqlite3_vtab_cursor ? cursor : VT.xCursor.get(cursor); - if (reset || !o.vTabState) o.vTabState = Object.assign(Object.create(ProtoCursor), { - rowid: 0, - names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) - }); - return o.vTabState; - }; - const dbg = () => {}; - const theModule = function f() { - return f.mod ??= new sqlite3.capi.sqlite3_module().setupModule({ - catchExceptions: true, - methods: { - xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr) { - dbg("xConnect"); - try { - const xcol = []; - Object.keys(cols).forEach((k) => { - xcol.push(k + " " + cols[k].type); - }); - const rc = capi.sqlite3_declare_vtab(pDb, "CREATE TABLE ignored(" + xcol.join(",") + ")"); - if (0 === rc) { - const t = VT.xVtab.create(ppVtab); - util.assert(t === VT.xVtab.get(wasm.peekPtr(ppVtab)), "output pointer check failed"); - } - return rc; - } catch (e) { - return VT.xErrror("xConnect", e, capi.SQLITE_ERROR); - } - }, - xCreate: wasm.ptr.null, - xDisconnect: function(pVtab) { - dbg("xDisconnect", ...arguments); - VT.xVtab.dispose(pVtab); - return 0; - }, - xOpen: function(pVtab, ppCursor) { - dbg("xOpen", ...arguments); - VT.xCursor.create(ppCursor); - return 0; - }, - xClose: function(pCursor) { - dbg("xClose", ...arguments); - const c = VT.xCursor.unget(pCursor); - delete c.vTabState; - c.dispose(); - return 0; - }, - xNext: function(pCursor) { - dbg("xNext", ...arguments); - const c = VT.xCursor.get(pCursor); - ++cursorState(c).rowid; - return 0; - }, - xColumn: function(pCursor, pCtx, iCol) { - dbg("xColumn", ...arguments); - const st = cursorState(pCursor); - const store = st.row(); - util.assert(store, "Unexpected xColumn call"); - switch (iCol) { - case cols.rowid.colId: - capi.sqlite3_result_int(pCtx, st.rowid); - break; - case cols.name.colId: - capi.sqlite3_result_text(pCtx, store.jzClass, -1, capi.SQLITE_TRANSIENT); - break; - case cols.nRef.colId: - capi.sqlite3_result_int(pCtx, store.refc); - break; - case cols.nOpen.colId: - capi.sqlite3_result_int(pCtx, store.files.length); - break; - case cols.isTransient.colId: - capi.sqlite3_result_int(pCtx, !!store.deleteAtRefc0); - break; - case cols.dbSize.colId: - capi.sqlite3_result_int(pCtx, storageGetDbSize(store)); - break; - default: - capi.sqlite3_result_error(pCtx, "Invalid column id: " + iCol); - return capi.SQLITE_RANGE; - } - return 0; - }, - xRowid: function(pCursor, ppRowid64) { - dbg("xRowid", ...arguments); - const st = cursorState(pCursor); - VT.xRowid(ppRowid64, st.rowid); - return 0; - }, - xEof: function(pCursor) { - const st = cursorState(pCursor); - dbg("xEof?=" + !st.row(), ...arguments); - return !st.row(); - }, - xFilter: function(pCursor, idxNum, idxCStr, argc, argv) { - dbg("xFilter", ...arguments); - cursorState(pCursor, true); - return 0; - }, - xBestIndex: function(pVtab, pIdxInfo) { - dbg("xBestIndex", ...arguments); - const pii = new capi.sqlite3_index_info(pIdxInfo); - pii.$estimatedRows = cache.storagePool.size; - pii.$estimatedCost = 1; - pii.dispose(); - return 0; - } - } - }); - }; - sqlite3.kvvfs.create_module = function(pDb, name = "sqlite_kvvfs") { - return capi.sqlite3_create_module(pDb, name, theModule(), wasm.ptr.null); - }; - } - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - /** - installOpfsVfs() returns a Promise which, on success, installs an - sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs - which accept a VFS. It is intended to be called via - sqlite3ApiBootstrap.initializers or an equivalent mechanism. - - The installed VFS uses the Origin-Private FileSystem API for - all file storage. On error it is rejected with an exception - explaining the problem. Reasons for rejection include, but are - not limited to: - - - The counterpart Worker (see below) could not be loaded. - - - The environment does not support OPFS. That includes when - this function is called from the main window thread. - - Significant notes and limitations: - - - The OPFS features used here are only available in dedicated Worker - threads. This file tries to detect that case, resulting in a - rejected Promise if those features do not seem to be available. - - - It requires the SharedArrayBuffer and Atomics classes, and the - former is only available if the HTTP server emits the so-called - COOP and COEP response headers. These features are required for - proxying OPFS's synchronous API via the synchronous interface - required by the sqlite3_vfs API. - - - This function may only be called a single time. When called, this - function removes itself from the sqlite3 object. - - All arguments to this function are for internal/development purposes - only. They do not constitute a public API and may change at any - time. - - The argument may optionally be a plain object with the following - configuration options: - - - proxyUri: name of the async proxy JS file. - - - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables - logging of errors. 2 enables logging of warnings and errors. 3 - additionally enables debugging info. Logging is performed - via the sqlite3.config.{log|warn|error}() functions. - - - sanityChecks (=false): if true, some basic sanity tests are run on - the OPFS VFS API after it's initialized, before the returned - Promise resolves. This is only intended for testing and - development of the VFS, not client-side use. - - On success, the Promise resolves to the top-most sqlite3 namespace - object and that object gets a new object installed in its - `opfs` property, containing several OPFS-specific utilities. - */ - const installOpfsVfs = function callee(options) { - if (!globalThis.SharedArrayBuffer || !globalThis.Atomics) return Promise.reject(/* @__PURE__ */ new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. The server must emit the COOP/COEP response headers to enable those. See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep")); - else if ("undefined" === typeof WorkerGlobalScope) return Promise.reject(/* @__PURE__ */ new Error("The OPFS sqlite3_vfs cannot run in the main thread because it requires Atomics.wait().")); - else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); - if (!options || "object" !== typeof options) options = Object.create(null); - const urlParams = new URL(globalThis.location.href).searchParams; - if (urlParams.has("opfs-disable")) return Promise.resolve(sqlite3); - if (void 0 === options.verbose) options.verbose = urlParams.has("opfs-verbose") ? +urlParams.get("opfs-verbose") || 2 : 1; - if (void 0 === options.sanityChecks) options.sanityChecks = urlParams.has("opfs-sanity-check"); - if (void 0 === options.proxyUri) options.proxyUri = callee.defaultProxyUri; - if ("function" === typeof options.proxyUri) options.proxyUri = options.proxyUri(); - return new Promise(function(promiseResolve_, promiseReject_) { - const loggers = [ - sqlite3.config.error, - sqlite3.config.warn, - sqlite3.config.log - ]; - const logImpl = (level, ...args) => { - if (options.verbose > level) loggers[level]("OPFS syncer:", ...args); - }; - const log = (...args) => logImpl(2, ...args); - const warn = (...args) => logImpl(1, ...args); - const error = (...args) => logImpl(0, ...args); - const toss = sqlite3.util.toss; - const capi = sqlite3.capi; - const util = sqlite3.util; - const wasm = sqlite3.wasm; - const sqlite3_vfs = capi.sqlite3_vfs; - const sqlite3_file = capi.sqlite3_file; - const sqlite3_io_methods = capi.sqlite3_io_methods; - /** - Generic utilities for working with OPFS. This will get filled out - by the Promise setup and, on success, installed as sqlite3.opfs. - - ACHTUNG: do not rely on these APIs in client code. They are - experimental and subject to change or removal as the - OPFS-specific sqlite3_vfs evolves. - */ - const opfsUtil = Object.create(null); - /** - Returns true if _this_ thread has access to the OPFS APIs. - */ - const thisThreadHasOPFS = () => { - return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory; - }; - /** - Not part of the public API. Solely for internal/development - use. - */ - opfsUtil.metrics = { - dump: function() { - let k, n = 0, t = 0, w = 0; - for (k in state.opIds) { - const m = metrics[k]; - n += m.count; - t += m.time; - w += m.wait; - m.avgTime = m.count && m.time ? m.time / m.count : 0; - m.avgWait = m.count && m.wait ? m.wait / m.count : 0; - } - sqlite3.config.log(globalThis.location.href, "metrics for", globalThis.location.href, ":", metrics, "\nTotal of", n, "op(s) for", t, "ms (incl. " + w + " ms of waiting on the async side)"); - sqlite3.config.log("Serialization metrics:", metrics.s11n); - W.postMessage({ type: "opfs-async-metrics" }); - }, - reset: function() { - let k; - const r = (m) => m.count = m.time = m.wait = 0; - for (k in state.opIds) r(metrics[k] = Object.create(null)); - let s = metrics.s11n = Object.create(null); - s = s.serialize = Object.create(null); - s.count = s.time = 0; - s = metrics.s11n.deserialize = Object.create(null); - s.count = s.time = 0; - } - }; - const opfsIoMethods = new sqlite3_io_methods(); - const opfsVfs = new sqlite3_vfs().addOnDispose(() => opfsIoMethods.dispose()); - let promiseWasRejected = void 0; - const promiseReject = (err) => { - promiseWasRejected = true; - opfsVfs.dispose(); - return promiseReject_(err); - }; - const promiseResolve = () => { - promiseWasRejected = false; - return promiseResolve_(sqlite3); - }; - const W = new Worker(new URL("sqlite3-opfs-async-proxy.js", import.meta.url)); - setTimeout(() => { - if (void 0 === promiseWasRejected) promiseReject(/* @__PURE__ */ new Error("Timeout while waiting for OPFS async proxy worker.")); - }, 4e3); - W._originalOnError = W.onerror; - W.onerror = function(err) { - error("Error initializing OPFS asyncer:", err); - promiseReject(/* @__PURE__ */ new Error("Loading OPFS async Worker failed for unknown reasons.")); - }; - const pDVfs = capi.sqlite3_vfs_find(null); - const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : null; - opfsIoMethods.$iVersion = 1; - opfsVfs.$iVersion = 2; - opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - opfsVfs.$mxPathname = 1024; - opfsVfs.$zName = wasm.allocCString("opfs"); - opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; - opfsVfs.addOnDispose("$zName", opfsVfs.$zName, "cleanup default VFS wrapper", () => dVfs ? dVfs.dispose() : null); - /** - Pedantic sidebar about opfsVfs.ondispose: the entries in that array - are items to clean up when opfsVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the opfsVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - /** - State which we send to the async-api Worker or share with it. - This object must initially contain only cloneable or sharable - objects. After the worker's "inited" message arrives, other types - of data may be added to it. - - For purposes of Atomics.wait() and Atomics.notify(), we use a - SharedArrayBuffer with one slot reserved for each of the API - proxy's methods. The sync side of the API uses Atomics.wait() - on the corresponding slot and the async side uses - Atomics.notify() on that slot. - - The approach of using a single SAB to serialize comms for all - instances might(?) lead to deadlock situations in multi-db - cases. We should probably have one SAB here with a single slot - for locking a per-file initialization step and then allocate a - separate SAB like the above one for each file. That will - require a bit of acrobatics but should be feasible. The most - problematic part is that xOpen() would have to use - postMessage() to communicate its SharedArrayBuffer, and mixing - that approach with Atomics.wait/notify() gets a bit messy. - */ - const state = Object.create(null); - state.verbose = options.verbose; - state.littleEndian = (() => { - const buffer = /* @__PURE__ */ new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true); - return new Int16Array(buffer)[0] === 256; - })(); - /** - asyncIdleWaitTime is how long (ms) to wait, in the async proxy, - for each Atomics.wait() when waiting on inbound VFS API calls. - We need to wake up periodically to give the thread a chance to - do other things. If this is too high (e.g. 500ms) then even two - workers/tabs can easily run into locking errors. Some multiple - of this value is also used for determining how long to wait on - lock contention to free up. - */ - state.asyncIdleWaitTime = 150; - /** - Whether the async counterpart should log exceptions to - the serialization channel. That produces a great deal of - noise for seemingly innocuous things like xAccess() checks - for missing files, so this option may have one of 3 values: - - 0 = no exception logging. - - 1 = only log exceptions for "significant" ops like xOpen(), - xRead(), and xWrite(). - - 2 = log all exceptions. - */ - state.asyncS11nExceptions = 1; - state.fileBufferSize = 1024 * 64; - state.sabS11nOffset = state.fileBufferSize; - /** - The size of the block in our SAB for serializing arguments and - result values. Needs to be large enough to hold serialized - values of any of the proxied APIs. Filenames are the largest - part but are limited to opfsVfs.$mxPathname bytes. We also - store exceptions there, so it needs to be long enough to hold - a reasonably long exception string. - */ - state.sabS11nSize = opfsVfs.$mxPathname * 2; - /** - The SAB used for all data I/O between the synchronous and - async halves (file i/o and arg/result s11n). - */ - state.sabIO = new SharedArrayBuffer(state.fileBufferSize + state.sabS11nSize); - state.opIds = Object.create(null); - const metrics = Object.create(null); - { - let i = 0; - state.opIds.whichOp = i++; - state.opIds.rc = i++; - state.opIds.xAccess = i++; - state.opIds.xClose = i++; - state.opIds.xDelete = i++; - state.opIds.xDeleteNoWait = i++; - state.opIds.xFileSize = i++; - state.opIds.xLock = i++; - state.opIds.xOpen = i++; - state.opIds.xRead = i++; - state.opIds.xSleep = i++; - state.opIds.xSync = i++; - state.opIds.xTruncate = i++; - state.opIds.xUnlock = i++; - state.opIds.xWrite = i++; - state.opIds.mkdir = i++; - state.opIds["opfs-async-metrics"] = i++; - state.opIds["opfs-async-shutdown"] = i++; - state.opIds.retry = i++; - state.sabOP = new SharedArrayBuffer(i * 4); - opfsUtil.metrics.reset(); - } - /** - SQLITE_xxx constants to export to the async worker - counterpart... - */ - state.sq3Codes = Object.create(null); - [ - "SQLITE_ACCESS_EXISTS", - "SQLITE_ACCESS_READWRITE", - "SQLITE_BUSY", - "SQLITE_CANTOPEN", - "SQLITE_ERROR", - "SQLITE_IOERR", - "SQLITE_IOERR_ACCESS", - "SQLITE_IOERR_CLOSE", - "SQLITE_IOERR_DELETE", - "SQLITE_IOERR_FSYNC", - "SQLITE_IOERR_LOCK", - "SQLITE_IOERR_READ", - "SQLITE_IOERR_SHORT_READ", - "SQLITE_IOERR_TRUNCATE", - "SQLITE_IOERR_UNLOCK", - "SQLITE_IOERR_WRITE", - "SQLITE_LOCK_EXCLUSIVE", - "SQLITE_LOCK_NONE", - "SQLITE_LOCK_PENDING", - "SQLITE_LOCK_RESERVED", - "SQLITE_LOCK_SHARED", - "SQLITE_LOCKED", - "SQLITE_MISUSE", - "SQLITE_NOTFOUND", - "SQLITE_OPEN_CREATE", - "SQLITE_OPEN_DELETEONCLOSE", - "SQLITE_OPEN_MAIN_DB", - "SQLITE_OPEN_READONLY" - ].forEach((k) => { - if (void 0 === (state.sq3Codes[k] = capi[k])) toss("Maintenance required: not found:", k); - }); - state.opfsFlags = Object.assign(Object.create(null), { - OPFS_UNLOCK_ASAP: 1, - OPFS_UNLINK_BEFORE_OPEN: 2, - defaultUnlockAsap: false - }); - /** - Runs the given operation (by name) in the async worker - counterpart, waits for its response, and returns the result - which the async worker writes to SAB[state.opIds.rc]. The - 2nd and subsequent arguments must be the arguments for the - async op. - */ - const opRun = (op, ...args) => { - const opNdx = state.opIds[op] || toss("Invalid op ID:", op); - state.s11n.serialize(...args); - Atomics.store(state.sabOPView, state.opIds.rc, -1); - Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); - Atomics.notify(state.sabOPView, state.opIds.whichOp); - const t = performance.now(); - while ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.rc, -1)); - const rc = Atomics.load(state.sabOPView, state.opIds.rc); - metrics[op].wait += performance.now() - t; - if (rc && state.asyncS11nExceptions) { - const err = state.s11n.deserialize(); - if (err) error(op + "() async error:", ...err); - } - return rc; - }; - /** - Not part of the public API. Only for test/development use. - */ - opfsUtil.debug = { - asyncShutdown: () => { - warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); - opRun("opfs-async-shutdown"); - }, - asyncRestart: () => { - warn("Attempting to restart OPFS VFS async listener. Might work, might not."); - W.postMessage({ type: "opfs-async-restart" }); - } - }; - const initS11n = () => { - /** - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ACHTUNG: this code is 100% duplicated in the other half of - this proxy! The documentation is maintained in the - "synchronous half". - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - This proxy de/serializes cross-thread function arguments and - output-pointer values via the state.sabIO SharedArrayBuffer, - using the region defined by (state.sabS11nOffset, - state.sabS11nOffset + state.sabS11nSize]. Only one dataset is - recorded at a time. - - This is not a general-purpose format. It only supports the - range of operations, and data sizes, needed by the - sqlite3_vfs and sqlite3_io_methods operations. Serialized - data are transient and this serialization algorithm may - change at any time. - - The data format can be succinctly summarized as: - - Nt...Td...D - - Where: - - - N = number of entries (1 byte) - - - t = type ID of first argument (1 byte) - - - ...T = type IDs of the 2nd and subsequent arguments (1 byte - each). - - - d = raw bytes of first argument (per-type size). - - - ...D = raw bytes of the 2nd and subsequent arguments (per-type - size). - - All types except strings have fixed sizes. Strings are stored - using their TextEncoder/TextDecoder representations. It would - arguably make more sense to store them as Int16Arrays of - their JS character values, but how best/fastest to get that - in and out of string form is an open point. Initial - experimentation with that approach did not gain us any speed. - - Historical note: this impl was initially about 1% this size by - using using JSON.stringify/parse(), but using fit-to-purpose - serialization saves considerable runtime. - */ - if (state.s11n) return state.s11n; - const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - state.s11n = Object.create(null); - const TypeIds = Object.create(null); - TypeIds.number = { - id: 1, - size: 8, - getter: "getFloat64", - setter: "setFloat64" - }; - TypeIds.bigint = { - id: 2, - size: 8, - getter: "getBigInt64", - setter: "setBigInt64" - }; - TypeIds.boolean = { - id: 3, - size: 4, - getter: "getInt32", - setter: "setInt32" - }; - TypeIds.string = { id: 4 }; - const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v); - const getTypeIdById = (tid) => { - switch (tid) { - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:", tid); - } - }; - /** - Returns an array of the deserialized state stored by the most - recent serialize() operation (from this thread or the - counterpart thread), or null if the serialization buffer is - empty. If passed a truthy argument, the serialization buffer - is cleared after deserialization. - */ - state.s11n.deserialize = function(clear = false) { - ++metrics.s11n.deserialize.count; - const t = performance.now(); - const argc = viewU8[0]; - const rc = argc ? [] : null; - if (argc) { - const typeIds = []; - let offset = 1, i, n, v; - for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset])); - for (i = 0; i < argc; ++i) { - const t = typeIds[i]; - if (t.getter) { - v = viewDV[t.getter](offset, state.littleEndian); - offset += t.size; - } else { - n = viewDV.getInt32(offset, state.littleEndian); - offset += 4; - v = textDecoder.decode(viewU8.slice(offset, offset + n)); - offset += n; - } - rc.push(v); - } - } - if (clear) viewU8[0] = 0; - metrics.s11n.deserialize.time += performance.now() - t; - return rc; - }; - /** - Serializes all arguments to the shared buffer for consumption - by the counterpart thread. - - This routine is only intended for serializing OPFS VFS - arguments and (in at least one special case) result values, - and the buffer is sized to be able to comfortably handle - those. - - If passed no arguments then it zeroes out the serialization - state. - */ - state.s11n.serialize = function(...args) { - const t = performance.now(); - ++metrics.s11n.serialize.count; - if (args.length) { - const typeIds = []; - let i = 0, offset = 1; - viewU8[0] = args.length & 255; - for (; i < args.length; ++i, ++offset) { - typeIds.push(getTypeId(args[i])); - viewU8[offset] = typeIds[i].id; - } - for (i = 0; i < args.length; ++i) { - const t = typeIds[i]; - if (t.setter) { - viewDV[t.setter](offset, args[i], state.littleEndian); - offset += t.size; - } else { - const s = textEncoder.encode(args[i]); - viewDV.setInt32(offset, s.byteLength, state.littleEndian); - offset += 4; - viewU8.set(s, offset); - offset += s.byteLength; - } - } - } else viewU8[0] = 0; - metrics.s11n.serialize.time += performance.now() - t; - }; - return state.s11n; - }; - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len = 16) { - if (!f._chars) { - f._chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for (; i < len; ++i) { - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(""); - }; - /** - Map of sqlite3_file pointers to objects constructed by xOpen(). - */ - const __openFiles = Object.create(null); - const opTimer = Object.create(null); - opTimer.op = void 0; - opTimer.start = void 0; - const mTimeStart = (op) => { - opTimer.start = performance.now(); - opTimer.op = op; - ++metrics[op].count; - }; - const mTimeEnd = () => metrics[opTimer.op].time += performance.now() - opTimer.start; - /** - Impls for the sqlite3_io_methods methods. Maintenance reminder: - members are in alphabetical order to simplify finding them. - */ - const ioSyncWrappers = { - xCheckReservedLock: function(pFile, pOut) { - /** - As of late 2022, only a single lock can be held on an OPFS - file. We have no way of checking whether any _other_ db - connection has a lock except by trying to obtain and (on - success) release a sync-handle for it, but doing so would - involve an inherent race condition. For the time being, - pending a better solution, we simply report whether the - given pFile is open. - - Update 2024-06-12: based on forum discussions, this - function now always sets pOut to 0 (false): - - https://sqlite.org/forum/forumpost/a2f573b00cda1372 - */ - wasm.poke(pOut, 0, "i32"); - return 0; - }, - xClose: function(pFile) { - mTimeStart("xClose"); - let rc = 0; - const f = __openFiles[pFile]; - if (f) { - delete __openFiles[pFile]; - rc = opRun("xClose", pFile); - if (f.sq3File) f.sq3File.dispose(); - } - mTimeEnd(); - return rc; - }, - xDeviceCharacteristics: function(pFile) { - return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; - }, - xFileControl: function(pFile, opId, pArg) { - return capi.SQLITE_NOTFOUND; - }, - xFileSize: function(pFile, pSz64) { - mTimeStart("xFileSize"); - let rc = opRun("xFileSize", pFile); - if (0 == rc) try { - const sz = state.s11n.deserialize()[0]; - wasm.poke(pSz64, sz, "i64"); - } catch (e) { - error("Unexpected error reading xFileSize() result:", e); - rc = state.sq3Codes.SQLITE_IOERR; - } - mTimeEnd(); - return rc; - }, - xLock: function(pFile, lockType) { - mTimeStart("xLock"); - const f = __openFiles[pFile]; - let rc = 0; - if (!f.lockType) { - rc = opRun("xLock", pFile, lockType); - if (0 === rc) f.lockType = lockType; - } else f.lockType = lockType; - mTimeEnd(); - return rc; - }, - xRead: function(pFile, pDest, n, offset64) { - mTimeStart("xRead"); - const f = __openFiles[pFile]; - let rc; - try { - rc = opRun("xRead", pFile, n, Number(offset64)); - if (0 === rc || capi.SQLITE_IOERR_SHORT_READ === rc) - /** - Results get written to the SharedArrayBuffer f.sabView. - Because the heap is _not_ a SharedArrayBuffer, we have - to copy the results. TypedArray.set() seems to be the - fastest way to copy this. */ - wasm.heap8u().set(f.sabView.subarray(0, n), Number(pDest)); - } catch (e) { - error("xRead(", arguments, ") failed:", e, f); - rc = capi.SQLITE_IOERR_READ; - } - mTimeEnd(); - return rc; - }, - xSync: function(pFile, flags) { - mTimeStart("xSync"); - ++metrics.xSync.count; - const rc = opRun("xSync", pFile, flags); - mTimeEnd(); - return rc; - }, - xTruncate: function(pFile, sz64) { - mTimeStart("xTruncate"); - const rc = opRun("xTruncate", pFile, Number(sz64)); - mTimeEnd(); - return rc; - }, - xUnlock: function(pFile, lockType) { - mTimeStart("xUnlock"); - const f = __openFiles[pFile]; - let rc = 0; - if (capi.SQLITE_LOCK_NONE === lockType && f.lockType) rc = opRun("xUnlock", pFile, lockType); - if (0 === rc) f.lockType = lockType; - mTimeEnd(); - return rc; - }, - xWrite: function(pFile, pSrc, n, offset64) { - mTimeStart("xWrite"); - const f = __openFiles[pFile]; - let rc; - try { - f.sabView.set(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n)); - rc = opRun("xWrite", pFile, n, Number(offset64)); - } catch (e) { - error("xWrite(", arguments, ") failed:", e, f); - rc = capi.SQLITE_IOERR_WRITE; - } - mTimeEnd(); - return rc; - } - }; - /** - Impls for the sqlite3_vfs methods. Maintenance reminder: members - are in alphabetical order to simplify finding them. - */ - const vfsSyncWrappers = { - xAccess: function(pVfs, zName, flags, pOut) { - mTimeStart("xAccess"); - const rc = opRun("xAccess", wasm.cstrToJs(zName)); - wasm.poke(pOut, rc ? 0 : 1, "i32"); - mTimeEnd(); - return 0; - }, - xCurrentTime: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); - return 0; - }, - xCurrentTimeInt64: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); - return 0; - }, - xDelete: function(pVfs, zName, doSyncDir) { - mTimeStart("xDelete"); - const rc = opRun("xDelete", wasm.cstrToJs(zName), doSyncDir, false); - mTimeEnd(); - return rc; - }, - xFullPathname: function(pVfs, zName, nOut, pOut) { - return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; - }, - xGetLastError: function(pVfs, nOut, pOut) { - warn("OPFS xGetLastError() has nothing sensible to return."); - return 0; - }, - xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { - mTimeStart("xOpen"); - let opfsFlags = 0; - if (0 === zName) zName = randomFilename(); - else if (wasm.isPtr(zName)) { - if (capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP; - if (capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; - zName = wasm.cstrToJs(zName); - } - const fh = Object.create(null); - fh.fid = pFile; - fh.filename = zName; - fh.sab = new SharedArrayBuffer(state.fileBufferSize); - fh.flags = flags; - fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) && !!(flags & capi.SQLITE_OPEN_READONLY); - const rc = opRun("xOpen", pFile, zName, flags, opfsFlags); - if (!rc) { - if (fh.readOnly) wasm.poke(pOutFlags, capi.SQLITE_OPEN_READONLY, "i32"); - __openFiles[pFile] = fh; - fh.sabView = state.sabFileBufView; - fh.sq3File = new sqlite3_file(pFile); - fh.sq3File.$pMethods = opfsIoMethods.pointer; - fh.lockType = capi.SQLITE_LOCK_NONE; - } - mTimeEnd(); - return rc; - } - }; - if (dVfs) { - opfsVfs.$xRandomness = dVfs.$xRandomness; - opfsVfs.$xSleep = dVfs.$xSleep; - } - if (!opfsVfs.$xRandomness) vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut) { - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; - return i; - }; - if (!opfsVfs.$xSleep) vfsSyncWrappers.xSleep = function(pVfs, ms) { - Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); - return 0; - }; - /** - Expects an OPFS file path. It gets resolved, such that ".." - components are properly expanded, and returned. If the 2nd arg - is true, the result is returned as an array of path elements, - else an absolute path string is returned. - */ - opfsUtil.getResolvedPath = function(filename, splitIt) { - const p = new URL(filename, "file://irrelevant").pathname; - return splitIt ? p.split("/").filter((v) => !!v) : p; - }; - /** - Takes the absolute path to a filesystem element. Returns an - array of [handleOfContainingDir, filename]. If the 2nd argument - is truthy then each directory element leading to the file is - created along the way. Throws if any creation or resolution - fails. - */ - opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false) { - const path = opfsUtil.getResolvedPath(absFilename, true); - const filename = path.pop(); - let dh = opfsUtil.rootDirectory; - for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); - return [dh, filename]; - }; - /** - Creates the given directory name, recursively, in - the OPFS filesystem. Returns true if it succeeds or the - directory already exists, else false. - */ - opfsUtil.mkdir = async function(absDirName) { - try { - await opfsUtil.getDirForFilename(absDirName + "/filepart", true); - return true; - } catch (e) { - return false; - } - }; - /** - Checks whether the given OPFS filesystem entry exists, - returning true if it does, false if it doesn't or if an - exception is intercepted while trying to make the - determination. - */ - opfsUtil.entryExists = async function(fsEntryName) { - try { - const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); - await dh.getFileHandle(fn); - return true; - } catch (e) { - return false; - } - }; - /** - Generates a random ASCII string, intended for use as a - temporary file name. Its argument is the length of the string, - defaulting to 16. - */ - opfsUtil.randomFilename = randomFilename; - /** - Returns a promise which resolves to an object which represents - all files and directories in the OPFS tree. The top-most object - has two properties: `dirs` is an array of directory entries - (described below) and `files` is a list of file names for all - files in that directory. - - Traversal starts at sqlite3.opfs.rootDirectory. - - Each `dirs` entry is an object in this form: - - ``` - { name: directoryName, - dirs: [...subdirs], - files: [...file names] - } - ``` - - The `files` and `subdirs` entries are always set but may be - empty arrays. - - The returned object has the same structure but its `name` is - an empty string. All returned objects are created with - Object.create(null), so have no prototype. - - Design note: the entries do not contain more information, - e.g. file sizes, because getting such info is not only - expensive but is subject to locking-related errors. - */ - opfsUtil.treeList = async function() { - const doDir = async function callee(dirHandle, tgt) { - tgt.name = dirHandle.name; - tgt.dirs = []; - tgt.files = []; - for await (const handle of dirHandle.values()) if ("directory" === handle.kind) { - const subDir = Object.create(null); - tgt.dirs.push(subDir); - await callee(handle, subDir); - } else tgt.files.push(handle.name); - }; - const root = Object.create(null); - await doDir(opfsUtil.rootDirectory, root); - return root; - }; - /** - Irrevocably deletes _all_ files in the current origin's OPFS. - Obviously, this must be used with great caution. It may throw - an exception if removal of anything fails (e.g. a file is - locked), but the precise conditions under which the underlying - APIs will throw are not documented (so we cannot tell you what - they are). - */ - opfsUtil.rmfr = async function() { - const dir = opfsUtil.rootDirectory, opt = { recurse: true }; - for await (const handle of dir.values()) dir.removeEntry(handle.name, opt); - }; - /** - Deletes the given OPFS filesystem entry. As this environment - has no notion of "current directory", the given name must be an - absolute path. If the 2nd argument is truthy, deletion is - recursive (use with caution!). - - The returned Promise resolves to true if the deletion was - successful, else false (but...). The OPFS API reports the - reason for the failure only in human-readable form, not - exceptions which can be type-checked to determine the - failure. Because of that... - - If the final argument is truthy then this function will - propagate any exception on error, rather than returning false. - */ - opfsUtil.unlink = async function(fsEntryName, recursive = false, throwOnError = false) { - try { - const [hDir, filenamePart] = await opfsUtil.getDirForFilename(fsEntryName, false); - await hDir.removeEntry(filenamePart, { recursive }); - return true; - } catch (e) { - if (throwOnError) throw new Error("unlink(", arguments[0], ") failed: " + e.message, { cause: e }); - return false; - } - }; - /** - Traverses the OPFS filesystem, calling a callback for each - entry. The argument may be either a callback function or an - options object with any of the following properties: - - - `callback`: function which gets called for each filesystem - entry. It gets passed 3 arguments: 1) the - FileSystemFileHandle or FileSystemDirectoryHandle of each - entry (noting that both are instanceof FileSystemHandle). 2) - the FileSystemDirectoryHandle of the parent directory. 3) the - current depth level, with 0 being at the top of the tree - relative to the starting directory. If the callback returns a - literal false, as opposed to any other falsy value, traversal - stops without an error. Any exceptions it throws are - propagated. Results are undefined if the callback manipulate - the filesystem (e.g. removing or adding entries) because the - how OPFS iterators behave in the face of such changes is - undocumented. - - - `recursive` [bool=true]: specifies whether to recurse into - subdirectories or not. Whether recursion is depth-first or - breadth-first is unspecified! - - - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] - specifies the starting directory. - - If this function is passed a function, it is assumed to be the - callback. - - Returns a promise because it has to (by virtue of being async) - but that promise has no specific meaning: the traversal it - performs is synchronous. The promise must be used to catch any - exceptions propagated by the callback, however. - */ - opfsUtil.traverse = async function(opt) { - const defaultOpt = { - recursive: true, - directory: opfsUtil.rootDirectory - }; - if ("function" === typeof opt) opt = { callback: opt }; - opt = Object.assign(defaultOpt, opt || {}); - (async function callee(dirHandle, depth) { - for await (const handle of dirHandle.values()) if (false === opt.callback(handle, dirHandle, depth)) return false; - else if (opt.recursive && "directory" === handle.kind) { - if (false === await callee(handle, depth + 1)) break; - } - })(opt.directory, 0); - }; - /** - impl of importDb() when it's given a function as its second - argument. - */ - const importDbChunked = async function(filename, callback) { - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - let sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(), nWrote = 0, chunk, checkedHeader = false; - try { - sah.truncate(0); - while (void 0 !== (chunk = await callback())) { - if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); - if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { - util.affirmDbHeader(chunk); - checkedHeader = true; - } - sah.write(chunk, { at: nWrote }); - nWrote += chunk.byteLength; - } - if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); - if (!checkedHeader) { - const header = new Uint8Array(20); - sah.read(header, { at: 0 }); - util.affirmDbHeader(header); - } - sah.write(new Uint8Array([1, 1]), { at: 18 }); - return nWrote; - } catch (e) { - await sah.close(); - sah = void 0; - await hDir.removeEntry(fnamePart).catch(() => {}); - throw e; - } finally { - if (sah) await sah.close(); - } - }; - /** - Asynchronously imports the given bytes (a byte array or - ArrayBuffer) into the given database file. - - Results are undefined if the given db name refers to an opened - db. - - If passed a function for its second argument, its behaviour - changes: imports its data in chunks fed to it by the given - callback function. It calls the callback (which may be async) - repeatedly, expecting either a Uint8Array or ArrayBuffer (to - denote new input) or undefined (to denote EOF). For so long as - the callback continues to return non-undefined, it will append - incoming data to the given VFS-hosted database file. When - called this way, the resolved value of the returned Promise is - the number of bytes written to the target file. - - It very specifically requires the input to be an SQLite3 - database and throws if that's not the case. It does so in - order to prevent this function from taking on a larger scope - than it is specifically intended to. i.e. we do not want it to - become a convenience for importing arbitrary files into OPFS. - - This routine rewrites the database header bytes in the output - file (not the input array) to force disabling of WAL mode. - - On error this throws and the state of the input file is - undefined (it depends on where the exception was triggered). - - On success, resolves to the number of bytes written. - */ - opfsUtil.importDb = async function(filename, bytes) { - if (bytes instanceof Function) return importDbChunked(filename, bytes); - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - util.affirmIsDb(bytes); - const n = bytes.byteLength; - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - let sah, nWrote = 0; - try { - sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(); - sah.truncate(0); - nWrote = sah.write(bytes, { at: 0 }); - if (nWrote != n) toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); - sah.write(new Uint8Array([1, 1]), { at: 18 }); - return nWrote; - } catch (e) { - if (sah) { - await sah.close(); - sah = void 0; - } - await hDir.removeEntry(fnamePart).catch(() => {}); - throw e; - } finally { - if (sah) await sah.close(); - } - }; - if (sqlite3.oo1) { - const OpfsDb = function(...args) { - const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); - opt.vfs = opfsVfs.$zName; - sqlite3.oo1.DB.dbCtorHelper.call(this, opt); - }; - OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); - sqlite3.oo1.OpfsDb = OpfsDb; - OpfsDb.importDb = opfsUtil.importDb; - sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback(opfsVfs.pointer, function(oo1Db, sqlite3) { - sqlite3.capi.sqlite3_busy_timeout(oo1Db, 1e4); - }); - } - const sanityCheck = function() { - const scope = wasm.scopedAllocPush(); - const sq3File = new sqlite3_file(); - try { - const fid = sq3File.pointer; - const openFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE | capi.SQLITE_OPEN_MAIN_DB; - const pOut = wasm.scopedAlloc(8); - const dbFile = "/sanity/check/file" + randomFilename(8); - const zDbFile = wasm.scopedAllocCString(dbFile); - let rc; - state.s11n.serialize("This is ä string."); - rc = state.s11n.deserialize(); - log("deserialize() says:", rc); - if ("This is ä string." !== rc[0]) toss("String d13n error."); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut, "i32"); - log("xAccess(", dbFile, ") exists ?=", rc); - rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, fid, openFlags, pOut); - log("open rc =", rc, "state.sabOPView[xOpen] =", state.sabOPView[state.opIds.xOpen]); - if (0 !== rc) { - error("open failed with code", rc); - return; - } - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut, "i32"); - if (!rc) toss("xAccess() failed to detect file."); - rc = ioSyncWrappers.xSync(sq3File.pointer, 0); - if (rc) toss("sync failed w/ rc", rc); - rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); - if (rc) toss("truncate failed w/ rc", rc); - wasm.poke(pOut, 0, "i64"); - rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); - if (rc) toss("xFileSize failed w/ rc", rc); - log("xFileSize says:", wasm.peek(pOut, "i64")); - rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); - if (rc) toss("xWrite() failed!"); - const readBuf = wasm.scopedAlloc(16); - rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); - wasm.poke(readBuf + 6, 0); - let jRead = wasm.cstrToJs(readBuf); - log("xRead() got:", jRead); - if ("sanity" !== jRead) toss("Unexpected xRead() value."); - if (vfsSyncWrappers.xSleep) { - log("xSleep()ing before close()ing..."); - vfsSyncWrappers.xSleep(opfsVfs.pointer, 2e3); - log("waking up from xSleep()"); - } - rc = ioSyncWrappers.xClose(fid); - log("xClose rc =", rc, "sabOPView =", state.sabOPView); - log("Deleting file:", dbFile); - vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 4660); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut, "i32"); - if (rc) toss("Expecting 0 from xAccess(", dbFile, ") after xDelete()."); - warn("End of OPFS sanity checks."); - } finally { - sq3File.dispose(); - wasm.scopedAllocPop(scope); - } - }; - W.onmessage = function({ data }) { - switch (data.type) { - case "opfs-unavailable": - promiseReject(new Error(data.payload.join(" "))); - break; - case "opfs-async-loaded": - W.postMessage({ - type: "opfs-async-init", - args: state - }); - break; - case "opfs-async-inited": - if (true === promiseWasRejected) break; - try { - sqlite3.vfs.installVfs({ - io: { - struct: opfsIoMethods, - methods: ioSyncWrappers - }, - vfs: { - struct: opfsVfs, - methods: vfsSyncWrappers - } - }); - state.sabOPView = new Int32Array(state.sabOP); - state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); - state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - initS11n(); - if (options.sanityChecks) { - warn("Running sanity checks because of opfs-sanity-check URL arg..."); - sanityCheck(); - } - if (thisThreadHasOPFS()) navigator.storage.getDirectory().then((d) => { - W.onerror = W._originalOnError; - delete W._originalOnError; - sqlite3.opfs = opfsUtil; - opfsUtil.rootDirectory = d; - log("End of OPFS sqlite3_vfs setup.", opfsVfs); - promiseResolve(); - }).catch(promiseReject); - else promiseResolve(); - } catch (e) { - error(e); - promiseReject(e); - } - break; - default: { - const errMsg = "Unexpected message from the OPFS async worker: " + JSON.stringify(data); - error(errMsg); - promiseReject(new Error(errMsg)); - break; - } - } - }; - }); - }; - installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js"; - globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3) => { - try { - let proxyJs = installOpfsVfs.defaultProxyUri; - if (sqlite3?.scriptInfo?.sqlite3Dir) installOpfsVfs.defaultProxyUri = sqlite3.scriptInfo.sqlite3Dir + proxyJs; - return installOpfsVfs().catch((e) => { - sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:", e.message); - }); - } catch (e) { - sqlite3.config.error("installOpfsVfs() exception:", e); - return Promise.reject(e); - } - }); - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - "use strict"; - const toss = sqlite3.util.toss; - const toss3 = sqlite3.util.toss3; - const initPromises = Object.create(null); - const capi = sqlite3.capi; - const util = sqlite3.util; - const wasm = sqlite3.wasm; - const SECTOR_SIZE = 4096; - const HEADER_MAX_PATH_SIZE = 512; - const HEADER_FLAGS_SIZE = 4; - const HEADER_DIGEST_SIZE = 8; - const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE; - const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE; - const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE; - const HEADER_OFFSET_DATA = SECTOR_SIZE; - const PERSISTENT_FILE_TYPES = capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_MAIN_JOURNAL | capi.SQLITE_OPEN_SUPER_JOURNAL | capi.SQLITE_OPEN_WAL; - const FLAG_COMPUTE_DIGEST_V2 = capi.SQLITE_OPEN_MEMORY; - /** Subdirectory of the VFS's space where "opaque" (randomly-named) - files are stored. Changing this effectively invalidates the data - stored under older names (orphaning it), so don't do that. */ - const OPAQUE_DIR_NAME = ".opaque"; - /** - Returns short a string of random alphanumeric characters - suitable for use as a random filename. - */ - const getRandomName = () => Math.random().toString(36).slice(2); - const textDecoder = new TextDecoder(); - const textEncoder = new TextEncoder(); - const optionDefaults = Object.assign(Object.create(null), { - name: "opfs-sahpool", - directory: void 0, - initialCapacity: 6, - clearOnInit: false, - verbosity: 2, - forceReinitIfPreviouslyFailed: false - }); - /** Logging routines, from most to least serious. */ - const loggers = [ - sqlite3.config.error, - sqlite3.config.warn, - sqlite3.config.log - ]; - sqlite3.config.log; - const warn = sqlite3.config.warn; - sqlite3.config.error; - const __mapVfsToPool = /* @__PURE__ */ new Map(); - const getPoolForVfs = (pVfs) => __mapVfsToPool.get(pVfs); - const setPoolForVfs = (pVfs, pool) => { - if (pool) __mapVfsToPool.set(pVfs, pool); - else __mapVfsToPool.delete(pVfs); - }; - const __mapSqlite3File = /* @__PURE__ */ new Map(); - const getPoolForPFile = (pFile) => __mapSqlite3File.get(pFile); - const setPoolForPFile = (pFile, pool) => { - if (pool) __mapSqlite3File.set(pFile, pool); - else __mapSqlite3File.delete(pFile); - }; - /** - Impls for the sqlite3_io_methods methods. Maintenance reminder: - members are in alphabetical order to simplify finding them. - */ - const ioMethods = { - xCheckReservedLock: function(pFile, pOut) { - const pool = getPoolForPFile(pFile); - pool.log("xCheckReservedLock"); - pool.storeErr(); - wasm.poke32(pOut, 1); - return 0; - }, - xClose: function(pFile) { - const pool = getPoolForPFile(pFile); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - if (file) try { - pool.log(`xClose ${file.path}`); - pool.mapS3FileToOFile(pFile, false); - file.sah.flush(); - if (file.flags & capi.SQLITE_OPEN_DELETEONCLOSE) pool.deletePath(file.path); - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - return 0; - }, - xDeviceCharacteristics: function(pFile) { - return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; - }, - xFileControl: function(pFile, opId, pArg) { - return capi.SQLITE_NOTFOUND; - }, - xFileSize: function(pFile, pSz64) { - const pool = getPoolForPFile(pFile); - pool.log(`xFileSize`); - const size = pool.getOFileForS3File(pFile).sah.getSize() - HEADER_OFFSET_DATA; - wasm.poke64(pSz64, BigInt(size)); - return 0; - }, - xLock: function(pFile, lockType) { - const pool = getPoolForPFile(pFile); - pool.log(`xLock ${lockType}`); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - file.lockType = lockType; - return 0; - }, - xRead: function(pFile, pDest, n, offset64) { - const pool = getPoolForPFile(pFile); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - pool.log(`xRead ${file.path} ${n} @ ${offset64}`); - try { - const nRead = file.sah.read(wasm.heap8u().subarray(Number(pDest), Number(pDest) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }); - if (nRead < n) { - wasm.heap8u().fill(0, Number(pDest) + nRead, Number(pDest) + n); - return capi.SQLITE_IOERR_SHORT_READ; - } - return 0; - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - }, - xSectorSize: function(pFile) { - return SECTOR_SIZE; - }, - xSync: function(pFile, flags) { - const pool = getPoolForPFile(pFile); - pool.log(`xSync ${flags}`); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - try { - file.sah.flush(); - return 0; - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - }, - xTruncate: function(pFile, sz64) { - const pool = getPoolForPFile(pFile); - pool.log(`xTruncate ${sz64}`); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - try { - file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64)); - return 0; - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - }, - xUnlock: function(pFile, lockType) { - const pool = getPoolForPFile(pFile); - pool.log("xUnlock"); - const file = pool.getOFileForS3File(pFile); - file.lockType = lockType; - return 0; - }, - xWrite: function(pFile, pSrc, n, offset64) { - const pool = getPoolForPFile(pFile); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - pool.log(`xWrite ${file.path} ${n} ${offset64}`); - try { - return n === file.sah.write(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }) ? 0 : toss("Unknown write() failure."); - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - } - }; - const opfsIoMethods = new capi.sqlite3_io_methods(); - opfsIoMethods.$iVersion = 1; - sqlite3.vfs.installVfs({ io: { - struct: opfsIoMethods, - methods: ioMethods - } }); - /** - Impls for the sqlite3_vfs methods. Maintenance reminder: members - are in alphabetical order to simplify finding them. - */ - const vfsMethods = { - xAccess: function(pVfs, zName, flags, pOut) { - const pool = getPoolForVfs(pVfs); - pool.storeErr(); - try { - const name = pool.getPath(zName); - wasm.poke32(pOut, pool.hasFilename(name) ? 1 : 0); - } catch (e) { - wasm.poke32(pOut, 0); - } - return 0; - }, - xCurrentTime: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); - return 0; - }, - xCurrentTimeInt64: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); - return 0; - }, - xDelete: function(pVfs, zName, doSyncDir) { - const pool = getPoolForVfs(pVfs); - pool.log(`xDelete ${wasm.cstrToJs(zName)}`); - pool.storeErr(); - try { - pool.deletePath(pool.getPath(zName)); - return 0; - } catch (e) { - pool.storeErr(e); - return capi.SQLITE_IOERR_DELETE; - } - }, - xFullPathname: function(pVfs, zName, nOut, pOut) { - return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; - }, - xGetLastError: function(pVfs, nOut, pOut) { - const pool = getPoolForVfs(pVfs); - const e = pool.popErr(); - pool.log(`xGetLastError ${nOut} e =`, e); - if (e) { - const scope = wasm.scopedAllocPush(); - try { - const [cMsg, n] = wasm.scopedAllocCString(e.message, true); - wasm.cstrncpy(pOut, cMsg, nOut); - if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); - } catch (e) { - return capi.SQLITE_NOMEM; - } finally { - wasm.scopedAllocPop(scope); - } - } - return e ? e.sqlite3Rc || capi.SQLITE_IOERR : 0; - }, - xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { - const pool = getPoolForVfs(pVfs); - try { - flags &= ~FLAG_COMPUTE_DIGEST_V2; - pool.log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`); - const path = zName && wasm.peek8(zName) ? pool.getPath(zName) : getRandomName(); - let sah = pool.getSAHForPath(path); - if (!sah && flags & capi.SQLITE_OPEN_CREATE) if (pool.getFileCount() < pool.getCapacity()) { - sah = pool.nextAvailableSAH(); - pool.setAssociatedPath(sah, path, flags); - } else toss("SAH pool is full. Cannot create file", path); - if (!sah) toss("file not found:", path); - const file = { - path, - flags, - sah - }; - pool.mapS3FileToOFile(pFile, file); - file.lockType = capi.SQLITE_LOCK_NONE; - const sq3File = new capi.sqlite3_file(pFile); - sq3File.$pMethods = opfsIoMethods.pointer; - sq3File.dispose(); - wasm.poke32(pOutFlags, flags); - return 0; - } catch (e) { - pool.storeErr(e); - return capi.SQLITE_CANTOPEN; - } - } - }; - /** - Creates, initializes, and returns an sqlite3_vfs instance for an - OpfsSAHPool. The argument is the VFS's name (JS string). - - Throws if the VFS name is already registered or if something - goes terribly wrong via sqlite3.vfs.installVfs(). - - Maintenance reminder: the only detail about the returned object - which is specific to any given OpfsSAHPool instance is the $zName - member. All other state is identical. - */ - const createOpfsVfs = function(vfsName) { - if (sqlite3.capi.sqlite3_vfs_find(vfsName)) toss3("VFS name is already registered:", vfsName); - const opfsVfs = new capi.sqlite3_vfs(); - const pDVfs = capi.sqlite3_vfs_find(null); - const dVfs = pDVfs ? new capi.sqlite3_vfs(pDVfs) : null; - opfsVfs.$iVersion = 2; - opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE; - opfsVfs.addOnDispose(opfsVfs.$zName = wasm.allocCString(vfsName), () => setPoolForVfs(opfsVfs.pointer, 0)); - if (dVfs) { - opfsVfs.$xRandomness = dVfs.$xRandomness; - opfsVfs.$xSleep = dVfs.$xSleep; - dVfs.dispose(); - } - if (!opfsVfs.$xRandomness && !vfsMethods.xRandomness) vfsMethods.xRandomness = function(pVfs, nOut, pOut) { - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; - return i; - }; - if (!opfsVfs.$xSleep && !vfsMethods.xSleep) vfsMethods.xSleep = (pVfs, ms) => 0; - sqlite3.vfs.installVfs({ vfs: { - struct: opfsVfs, - methods: vfsMethods - } }); - return opfsVfs; - }; - /** - Class for managing OPFS-related state for the - OPFS SharedAccessHandle Pool sqlite3_vfs. - */ - class OpfsSAHPool { - vfsDir; - #dhVfsRoot; - #dhOpaque; - #dhVfsParent; - #mapSAHToName = /* @__PURE__ */ new Map(); - #mapFilenameToSAH = /* @__PURE__ */ new Map(); - #availableSAH = /* @__PURE__ */ new Set(); - #mapS3FileToOFile_ = /* @__PURE__ */ new Map(); - /** Buffer used by [sg]etAssociatedPath(). */ - #apBody = new Uint8Array(HEADER_CORPUS_SIZE); - #dvBody; - #cVfs; - #verbosity; - constructor(options = Object.create(null)) { - this.#verbosity = options.verbosity ?? optionDefaults.verbosity; - this.vfsName = options.name || optionDefaults.name; - this.#cVfs = createOpfsVfs(this.vfsName); - setPoolForVfs(this.#cVfs.pointer, this); - this.vfsDir = options.directory || "." + this.vfsName; - this.#dvBody = new DataView(this.#apBody.buffer, this.#apBody.byteOffset); - this.isReady = this.reset(!!(options.clearOnInit ?? optionDefaults.clearOnInit)).then(() => { - if (this.$error) throw this.$error; - return this.getCapacity() ? Promise.resolve(void 0) : this.addCapacity(options.initialCapacity || optionDefaults.initialCapacity); - }); - } - #logImpl(level, ...args) { - if (this.#verbosity > level) loggers[level](this.vfsName + ":", ...args); - } - log(...args) { - this.#logImpl(2, ...args); - } - warn(...args) { - this.#logImpl(1, ...args); - } - error(...args) { - this.#logImpl(0, ...args); - } - getVfs() { - return this.#cVfs; - } - getCapacity() { - return this.#mapSAHToName.size; - } - getFileCount() { - return this.#mapFilenameToSAH.size; - } - getFileNames() { - const rc = []; - for (const n of this.#mapFilenameToSAH.keys()) rc.push(n); - return rc; - } - /** - Adds n files to the pool's capacity. This change is - persistent across settings. Returns a Promise which resolves - to the new capacity. - */ - async addCapacity(n) { - for (let i = 0; i < n; ++i) { - const name = getRandomName(); - const ah = await (await this.#dhOpaque.getFileHandle(name, { create: true })).createSyncAccessHandle(); - this.#mapSAHToName.set(ah, name); - this.setAssociatedPath(ah, "", 0); - } - return this.getCapacity(); - } - /** - Reduce capacity by n, but can only reduce up to the limit - of currently-available SAHs. Returns a Promise which resolves - to the number of slots really removed. - */ - async reduceCapacity(n) { - let nRm = 0; - for (const ah of Array.from(this.#availableSAH)) { - if (nRm === n || this.getFileCount() === this.getCapacity()) break; - const name = this.#mapSAHToName.get(ah); - ah.close(); - await this.#dhOpaque.removeEntry(name); - this.#mapSAHToName.delete(ah); - this.#availableSAH.delete(ah); - ++nRm; - } - return nRm; - } - /** - Releases all currently-opened SAHs. The only legal operation - after this is acquireAccessHandles() or (if this is called from - pauseVfs()) either of isPaused() or unpauseVfs(). - */ - releaseAccessHandles() { - for (const ah of this.#mapSAHToName.keys()) ah.close(); - this.#mapSAHToName.clear(); - this.#mapFilenameToSAH.clear(); - this.#availableSAH.clear(); - } - /** - Opens all files under this.vfsDir/this.#dhOpaque and acquires a - SAH for each. Returns a Promise which resolves to no value but - completes once all SAHs are acquired. If acquiring an SAH - throws, this.$error will contain the corresponding Error - object. - - If it throws, it releases any SAHs which it may have - acquired before the exception was thrown, leaving the VFS in a - well-defined but unusable state. - - If clearFiles is true, the client-stored state of each file is - cleared when its handle is acquired, including its name, flags, - and any data stored after the metadata block. - */ - async acquireAccessHandles(clearFiles = false) { - const files = []; - for await (const [name, h] of this.#dhOpaque) if ("file" === h.kind) files.push([name, h]); - return Promise.all(files.map(async ([name, h]) => { - try { - const ah = await h.createSyncAccessHandle(); - this.#mapSAHToName.set(ah, name); - if (clearFiles) { - ah.truncate(HEADER_OFFSET_DATA); - this.setAssociatedPath(ah, "", 0); - } else { - const path = this.getAssociatedPath(ah); - if (path) this.#mapFilenameToSAH.set(path, ah); - else this.#availableSAH.add(ah); - } - } catch (e) { - this.storeErr(e); - this.releaseAccessHandles(); - throw e; - } - })); - } - /** - Given an SAH, returns the client-specified name of - that file by extracting it from the SAH's header. - - On error, it disassociates SAH from the pool and - returns an empty string. - */ - getAssociatedPath(sah) { - sah.read(this.#apBody, { at: 0 }); - const flags = this.#dvBody.getUint32(HEADER_OFFSET_FLAGS); - if (this.#apBody[0] && (flags & capi.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) { - warn(`Removing file with unexpected flags ${flags.toString(16)}`, this.#apBody); - this.setAssociatedPath(sah, "", 0); - return ""; - } - const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4); - sah.read(fileDigest, { at: HEADER_OFFSET_DIGEST }); - const compDigest = this.computeDigest(this.#apBody, flags); - if (fileDigest.every((v, i) => v === compDigest[i])) { - const pathBytes = this.#apBody.findIndex((v) => 0 === v); - if (0 === pathBytes) sah.truncate(HEADER_OFFSET_DATA); - return pathBytes ? textDecoder.decode(this.#apBody.subarray(0, pathBytes)) : ""; - } else { - warn("Disassociating file with bad digest."); - this.setAssociatedPath(sah, "", 0); - return ""; - } - } - /** - Stores the given client-defined path and SQLITE_OPEN_xyz flags - into the given SAH. If path is an empty string then the file is - disassociated from the pool but its previous name is preserved - in the metadata. - */ - setAssociatedPath(sah, path, flags) { - const enc = textEncoder.encodeInto(path, this.#apBody); - if (HEADER_MAX_PATH_SIZE <= enc.written + 1) toss("Path too long:", path); - if (path && flags) flags |= FLAG_COMPUTE_DIGEST_V2; - this.#apBody.fill(0, enc.written, HEADER_MAX_PATH_SIZE); - this.#dvBody.setUint32(HEADER_OFFSET_FLAGS, flags); - const digest = this.computeDigest(this.#apBody, flags); - sah.write(this.#apBody, { at: 0 }); - sah.write(digest, { at: HEADER_OFFSET_DIGEST }); - sah.flush(); - if (path) { - this.#mapFilenameToSAH.set(path, sah); - this.#availableSAH.delete(sah); - } else { - sah.truncate(HEADER_OFFSET_DATA); - this.#availableSAH.add(sah); - } - } - /** - Computes a digest for the given byte array and returns it as a - two-element Uint32Array. This digest gets stored in the - metadata for each file as a validation check. Changing this - algorithm invalidates all existing databases for this VFS, so - don't do that. - - See the docs for FLAG_COMPUTE_DIGEST_V2 for more details. - */ - computeDigest(byteArray, fileFlags) { - if (fileFlags & FLAG_COMPUTE_DIGEST_V2) { - let h1 = 3735928559; - let h2 = 1103547991; - for (const v of byteArray) { - h1 = Math.imul(h1 ^ v, 2654435761); - h2 = Math.imul(h2 ^ v, 104729); - } - return new Uint32Array([h1 >>> 0, h2 >>> 0]); - } else return new Uint32Array([0, 0]); - } - /** - Re-initializes the state of the SAH pool, releasing and - re-acquiring all handles. - - See acquireAccessHandles() for the specifics of the clearFiles - argument. - */ - async reset(clearFiles) { - await this.isReady; - let h = await navigator.storage.getDirectory(), prev; - for (const d of this.vfsDir.split("/")) if (d) { - prev = h; - h = await h.getDirectoryHandle(d, { create: true }); - } - this.#dhVfsRoot = h; - this.#dhVfsParent = prev; - this.#dhOpaque = await this.#dhVfsRoot.getDirectoryHandle(OPAQUE_DIR_NAME, { create: true }); - this.releaseAccessHandles(); - return this.acquireAccessHandles(clearFiles); - } - /** - Returns the pathname part of the given argument, - which may be any of: - - - a URL object - - A JS string representing a file name - - Wasm C-string representing a file name - - All "../" parts and duplicate slashes are resolve/removed from - the returned result. - */ - getPath(arg) { - if (wasm.isPtr(arg)) arg = wasm.cstrToJs(arg); - return (arg instanceof URL ? arg : new URL(arg, "file://localhost/")).pathname; - } - /** - Removes the association of the given client-specified file - name (JS string) from the pool. Returns true if a mapping - is found, else false. - */ - deletePath(path) { - const sah = this.#mapFilenameToSAH.get(path); - if (sah) { - this.#mapFilenameToSAH.delete(path); - this.setAssociatedPath(sah, "", 0); - } - return !!sah; - } - /** - Sets e (an Error object) as this object's current error. Pass a - falsy (or no) value to clear it. If code is truthy it is - assumed to be an SQLITE_xxx result code, defaulting to - SQLITE_IOERR if code is falsy. - - Returns the 2nd argument. - */ - storeErr(e, code) { - if (e) { - e.sqlite3Rc = code || capi.SQLITE_IOERR; - this.error(e); - } - this.$error = e; - return code; - } - /** - Pops this object's Error object and returns - it (a falsy value if no error is set). - */ - popErr() { - const rc = this.$error; - this.$error = void 0; - return rc; - } - /** - Returns the next available SAH without removing - it from the set. - */ - nextAvailableSAH() { - const [rc] = this.#availableSAH.keys(); - return rc; - } - /** - Given an (sqlite3_file*), returns the mapped - xOpen file object. - */ - getOFileForS3File(pFile) { - return this.#mapS3FileToOFile_.get(pFile); - } - /** - Maps or unmaps (if file is falsy) the given (sqlite3_file*) - to an xOpen file object and to this pool object. - */ - mapS3FileToOFile(pFile, file) { - if (file) { - this.#mapS3FileToOFile_.set(pFile, file); - setPoolForPFile(pFile, this); - } else { - this.#mapS3FileToOFile_.delete(pFile); - setPoolForPFile(pFile, false); - } - } - /** - Returns true if the given client-defined file name is in this - object's name-to-SAH map. - */ - hasFilename(name) { - return this.#mapFilenameToSAH.has(name); - } - /** - Returns the SAH associated with the given - client-defined file name. - */ - getSAHForPath(path) { - return this.#mapFilenameToSAH.get(path); - } - /** - Removes this object's sqlite3_vfs registration and shuts down - this object, releasing all handles, mappings, and whatnot, - including deleting its data directory. There is currently no - way to "revive" the object and reaquire its - resources. Similarly, there is no recovery strategy if removal - of any given SAH fails, so such errors are ignored by this - function. - - This function is intended primarily for testing. - - Resolves to true if it did its job, false if the - VFS has already been shut down. - - @see pauseVfs() - @see unpauseVfs() - */ - async removeVfs() { - if (!this.#cVfs.pointer || !this.#dhOpaque) return false; - capi.sqlite3_vfs_unregister(this.#cVfs.pointer); - this.#cVfs.dispose(); - delete initPromises[this.vfsName]; - try { - this.releaseAccessHandles(); - await this.#dhVfsRoot.removeEntry(OPAQUE_DIR_NAME, { recursive: true }); - this.#dhOpaque = void 0; - await this.#dhVfsParent.removeEntry(this.#dhVfsRoot.name, { recursive: true }); - this.#dhVfsRoot = this.#dhVfsParent = void 0; - } catch (e) { - sqlite3.config.error(this.vfsName, "removeVfs() failed with no recovery strategy:", e); - } - return true; - } - /** - "Pauses" this VFS by unregistering it from SQLite and - relinquishing all open SAHs, leaving the associated files - intact. If this object is already paused, this is a - no-op. Returns this object. - - This function throws if SQLite has any opened file handles - hosted by this VFS, as the alternative would be to invoke - Undefined Behavior by closing file handles out from under the - library. Similarly, automatically closing any database handles - opened by this VFS would invoke Undefined Behavior in - downstream code which is holding those pointers. - - If this function throws due to open file handles then it has - no side effects. If the OPFS API throws while closing handles - then the VFS is left in an undefined state. - - @see isPaused() - @see unpauseVfs() - */ - pauseVfs() { - if (this.#mapS3FileToOFile_.size > 0) sqlite3.SQLite3Error.toss(capi.SQLITE_MISUSE, "Cannot pause VFS", this.vfsName, "because it has opened files."); - if (this.#mapSAHToName.size > 0) { - capi.sqlite3_vfs_unregister(this.vfsName); - this.releaseAccessHandles(); - } - return this; - } - /** - Returns true if this pool is currently paused else false. - - @see pauseVfs() - @see unpauseVfs() - */ - isPaused() { - return 0 === this.#mapSAHToName.size; - } - /** - "Unpauses" this VFS, reacquiring all SAH's and (if successful) - re-registering it with SQLite. This is a no-op if the VFS is - not currently paused. - - The returned Promise resolves to this object. See - acquireAccessHandles() for how it behaves if it throws due to - SAH acquisition failure. - - @see isPaused() - @see pauseVfs() - */ - async unpauseVfs() { - if (0 === this.#mapSAHToName.size) return this.acquireAccessHandles(false).then(() => capi.sqlite3_vfs_register(this.#cVfs, 0), this); - return this; - } - //! Documented elsewhere in this file. - exportFile(name) { - const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:", name); - const n = sah.getSize() - HEADER_OFFSET_DATA; - const b = new Uint8Array(n > 0 ? n : 0); - if (n > 0) { - const nRead = sah.read(b, { at: HEADER_OFFSET_DATA }); - if (nRead != n) toss("Expected to read " + n + " bytes but read " + nRead + "."); - } - return b; - } - //! Impl for importDb() when its 2nd arg is a function. - async importDbChunked(name, callback) { - const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); - sah.truncate(0); - let nWrote = 0, chunk, checkedHeader = false; - try { - while (void 0 !== (chunk = await callback())) { - if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); - if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { - util.affirmDbHeader(chunk); - checkedHeader = true; - } - sah.write(chunk, { at: HEADER_OFFSET_DATA + nWrote }); - nWrote += chunk.byteLength; - } - if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); - if (!checkedHeader) { - const header = new Uint8Array(20); - sah.read(header, { at: 0 }); - util.affirmDbHeader(header); - } - sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); - } catch (e) { - this.setAssociatedPath(sah, "", 0); - throw e; - } - this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); - return nWrote; - } - //! Documented elsewhere in this file. - importDb(name, bytes) { - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - else if (bytes instanceof Function) return this.importDbChunked(name, bytes); - const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); - const n = bytes.byteLength; - if (n < 512 || n % 512 != 0) toss("Byte array size is invalid for an SQLite db."); - const header = "SQLite format 3"; - for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss("Input does not contain an SQLite database header."); - const nWrote = sah.write(bytes, { at: HEADER_OFFSET_DATA }); - if (nWrote != n) { - this.setAssociatedPath(sah, "", 0); - toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); - } else { - sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); - this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); - } - return nWrote; - } - } - /** - A OpfsSAHPoolUtil instance is exposed to clients in order to - manipulate an OpfsSAHPool object without directly exposing that - object and allowing for some semantic changes compared to that - class. - - Class docs are in the client-level docs for - installOpfsSAHPoolVfs(). - */ - class OpfsSAHPoolUtil { - #p; - constructor(sahPool) { - this.#p = sahPool; - this.vfsName = sahPool.vfsName; - } - async addCapacity(n) { - return this.#p.addCapacity(n); - } - async reduceCapacity(n) { - return this.#p.reduceCapacity(n); - } - getCapacity() { - return this.#p.getCapacity(this.#p); - } - getFileCount() { - return this.#p.getFileCount(); - } - getFileNames() { - return this.#p.getFileNames(); - } - async reserveMinimumCapacity(min) { - const c = this.#p.getCapacity(); - return c < min ? this.#p.addCapacity(min - c) : c; - } - exportFile(name) { - return this.#p.exportFile(name); - } - importDb(name, bytes) { - return this.#p.importDb(name, bytes); - } - async wipeFiles() { - return this.#p.reset(true); - } - unlink(filename) { - return this.#p.deletePath(filename); - } - async removeVfs() { - return this.#p.removeVfs(); - } - pauseVfs() { - this.#p.pauseVfs(); - return this; - } - async unpauseVfs() { - return this.#p.unpauseVfs().then(() => this); - } - isPaused() { - return this.#p.isPaused(); - } - } - /** - Returns a resolved Promise if the current environment - has a "fully-sync" SAH impl, else a rejected Promise. - */ - const apiVersionCheck = async () => { - const dh = await navigator.storage.getDirectory(); - const fn = ".opfs-sahpool-sync-check-" + getRandomName(); - const close = (await (await dh.getFileHandle(fn, { create: true })).createSyncAccessHandle()).close(); - await close; - await dh.removeEntry(fn); - if (close?.then) toss("The local OPFS API is too old for opfs-sahpool:", "it has an async FileSystemSyncAccessHandle.close() method."); - return true; - }; - /** - installOpfsSAHPoolVfs() asynchronously initializes the OPFS - SyncAccessHandle (a.k.a. SAH) Pool VFS. It returns a Promise which - either resolves to a utility object described below or rejects with - an Error value. - - Initialization of this VFS is not automatic because its - registration requires that it lock all resources it - will potentially use, even if client code does not want - to use them. That, in turn, can lead to locking errors - when, for example, one page in a given origin has loaded - this VFS but does not use it, then another page in that - origin tries to use the VFS. If the VFS were automatically - registered, the second page would fail to load the VFS - due to OPFS locking errors. - - If this function is called more than once with a given "name" - option (see below), it will return the same Promise. Calls for - different names will return different Promises which resolve to - independent objects and refer to different VFS registrations. - - On success, the resulting Promise resolves to a utility object - which can be used to query and manipulate the pool. Its API is - described at the end of these docs. - - This function accepts an options object to configure certain - parts but it is only acknowledged for the very first call for - each distinct name and ignored for all subsequent calls with that - same name. - - The options, in alphabetical order: - - - `clearOnInit`: (default=false) if truthy, contents and filename - mapping are removed from each SAH it is acquired during - initialization of the VFS, leaving the VFS's storage in a pristine - state. Use this only for databases which need not survive a page - reload. - - - `initialCapacity`: (default=6) Specifies the default capacity of - the VFS. This should not be set unduly high because the VFS has - to open (and keep open) a file for each entry in the pool. This - setting only has an effect when the pool is initially empty. It - does not have any effect if a pool already exists. - - - `directory`: (default="."+`name`) Specifies the OPFS directory - name in which to store metadata for the `"opfs-sahpool"` - sqlite3_vfs. Only one instance of this VFS can be installed per - JavaScript engine, and any two engines with the same storage - directory name will collide with each other, leading to locking - errors and the inability to register the VFS in the second and - subsequent engine. Using a different directory name for each - application enables different engines in the same HTTP origin to - co-exist, but their data are invisible to each other. Changing - this name will effectively orphan any databases stored under - previous names. The default is unspecified but descriptive. This - option may contain multiple path elements, e.g. "foo/bar/baz", - and they are created automatically. In practice there should be - no driving need to change this. ACHTUNG: all files in this - directory are assumed to be managed by the VFS. Do not place - other files in that directory, as they may be deleted or - otherwise modified by the VFS. - - - `name`: (default="opfs-sahpool") sets the name to register this - VFS under. Normally this should not be changed, but it is - possible to register this VFS under multiple names so long as - each has its own separate directory to work from. The storage for - each is invisible to all others. The name must be a string - compatible with `sqlite3_vfs_register()` and friends and suitable - for use in URI-style database file names. - - Achtung: if a custom `name` is provided, a custom `directory` - must also be provided if any other instance is registered with - the default directory. If no directory is explicitly provided - then a directory name is synthesized from the `name` option. - - - - `forceReinitIfPreviouslyFailed`: (default=`false`) Is a fallback option - to assist in working around certain flaky environments which may - mysteriously fail to permit access to OPFS sync access handles on - an initial attempt but permit it on a second attemp. This option - should never be used but is provided for those who choose to - throw caution to the wind and trust such environments. If this - option is truthy _and_ the previous attempt to initialize this - VFS with the same `name` failed, the VFS will attempt to - initialize a second time instead of returning the cached - failure. See discussion at: - - - - Peculiarities of this VFS vis a vis other SQLite VFSes: - - - Paths given to it _must_ be absolute. Relative paths will not - be properly recognized. This is arguably a bug but correcting it - requires some hoop-jumping in routines which have no business - doing such tricks. (2026-01-19 (2.5 years later): the specifics - are lost to history, but this was a side effect of xOpen() - receiving an immutable C-string filename, to which no implicit - "/" can be prefixed without causing a discrepancy between what - the user provided and what the VFS stores. Its conceivable that - that quirk could be glossed over in xFullPathname(), but - regressions when doing so cannot be ruled out, so there are no - current plans to change this behavior.) - - - It is possible to install multiple instances under different - names, each sandboxed from one another inside their own private - directory. This feature exists primarily as a way for disparate - applications within a given HTTP origin to use this VFS without - introducing locking issues between them. - - - The API for the utility object passed on by this function's - Promise, in alphabetical order... - - - [async] number addCapacity(n) - - Adds `n` entries to the current pool. This change is persistent - across sessions so should not be called automatically at each app - startup (but see `reserveMinimumCapacity()`). Its returned Promise - resolves to the new capacity. Because this operation is necessarily - asynchronous, the C-level VFS API cannot call this on its own as - needed. - - - byteArray exportFile(name) - - Synchronously reads the contents of the given file into a Uint8Array - and returns it. This will throw if the given name is not currently - in active use or on I/O error. Note that the given name is _not_ - visible directly in OPFS (or, if it is, it's not from this VFS). - - - number getCapacity() - - Returns the number of files currently contained - in the SAH pool. The default capacity is only large enough for one - or two databases and their associated temp files. - - - number getFileCount() - - Returns the number of files from the pool currently allocated to - slots. This is not the same as the files being "opened". - - - array getFileNames() - - Returns an array of the names of the files currently allocated to - slots. This list is the same length as getFileCount(). - - - void importDb(name, bytes) - - Imports the contents of an SQLite database, provided as a byte - array or ArrayBuffer, under the given name, overwriting any - existing content. Throws if the pool has no available file slots, - on I/O error, or if the input does not appear to be a - database. In the latter case, only a cursory examination is made. - Results are undefined if the given db name refers to an opened - db. Note that this routine is _only_ for importing database - files, not arbitrary files, the reason being that this VFS will - automatically clean up any non-database files so importing them - is pointless. - - If passed a function for its second argument, its behavior - changes to asynchronous and it imports its data in chunks fed to - it by the given callback function. It calls the callback (which - may be async) repeatedly, expecting either a Uint8Array or - ArrayBuffer (to denote new input) or undefined (to denote - EOF). For so long as the callback continues to return - non-undefined, it will append incoming data to the given - VFS-hosted database file. The result of the resolved Promise when - called this way is the size of the resulting database. - - On success this routine rewrites the database header bytes in the - output file (not the input array) to force disabling of WAL mode. - - On a write error, the handle is removed from the pool and made - available for re-use. - - - [async] number reduceCapacity(n) - - Removes up to `n` entries from the pool, with the caveat that it can - only remove currently-unused entries. It returns a Promise which - resolves to the number of entries actually removed. - - - [async] boolean removeVfs() - - Unregisters the opfs-sahpool VFS and removes its directory from OPFS - (which means that _all client content_ is removed). After calling - this, the VFS may no longer be used and there is no way to re-add it - aside from reloading the current JavaScript context. - - Results are undefined if a database is currently in use with this - VFS. - - The returned Promise resolves to true if it performed the removal - and false if the VFS was not installed. - - If the VFS has a multi-level directory, e.g. "/foo/bar/baz", _only_ - the bottom-most directory is removed because this VFS cannot know for - certain whether the higher-level directories contain data which - should be removed. - - - [async] number reserveMinimumCapacity(min) - - If the current capacity is less than `min`, the capacity is - increased to `min`, else this returns with no side effects. The - resulting Promise resolves to the new capacity. - - - boolean unlink(filename) - - If a virtual file exists with the given name, disassociates it from - the pool and returns true, else returns false without side - effects. Results are undefined if the file is currently in active - use. - - - string vfsName - - The SQLite VFS name under which this pool's VFS is registered. - - - [async] void wipeFiles() - - Clears all client-defined state of all SAHs and makes all of them - available for re-use by the pool. Results are undefined if any such - handles are currently in use, e.g. by an sqlite3 db. - - APIs specific to the "pause" capability (added in version 3.49): - - Summary: "pausing" the VFS disassociates it from SQLite and - relinquishes its SAHs so that they may be opened by another - instance of this VFS (running in a separate tab/page or Worker). - "Unpausing" it takes back control, if able. - - - pauseVfs() - - "Pauses" this VFS by unregistering it from SQLite and - relinquishing all open SAHs, leaving the associated files intact. - This enables pages/tabs to coordinate semi-concurrent usage of - this VFS. If this object is already paused, this is a - no-op. Returns this object. Throws if SQLite has any opened file - handles hosted by this VFS. If this function throws due to open - file handles then it has no side effects. If the OPFS API throws - while closing handles then the VFS is left in an undefined state. - - - isPaused() - - Returns true if this VFS is paused, else false. - - - [async] unpauseVfs() - - Restores the VFS to an active state after having called - pauseVfs() on it. This is a no-op if the VFS is not paused. The - returned Promise resolves to this object on success. A rejected - Promise means there was a problem reacquiring the SAH handles - (possibly because they're in use by another instance or have - since been removed). Generically speaking, there is no recovery - strategy for that type of error, but if the problem is simply - that the OPFS files are locked, then a later attempt to unpause - it, made after the concurrent instance releases the SAHs, may - recover from the situation. - */ - sqlite3.installOpfsSAHPoolVfs = async function(options = Object.create(null)) { - options = Object.assign(Object.create(null), optionDefaults, options || {}); - const vfsName = options.name; - if (options.$testThrowPhase1) throw options.$testThrowPhase1; - if (initPromises[vfsName]) try { - return await initPromises[vfsName]; - } catch (e) { - if (options.forceReinitIfPreviouslyFailed) delete initPromises[vfsName]; - else throw e; - } - if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return initPromises[vfsName] = Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); - /** - Maintenance reminder: the order of ASYNC ops in this function - is significant. We need to have them all chained at the very - end in order to be able to catch a race condition where - installOpfsSAHPoolVfs() is called twice in rapid succession, - e.g.: - - installOpfsSAHPoolVfs().then(console.warn.bind(console)); - installOpfsSAHPoolVfs().then(console.warn.bind(console)); - - If the timing of the async calls is not "just right" then that - second call can end up triggering the init a second time and chaos - ensues. - */ - return initPromises[vfsName] = apiVersionCheck().then(async function() { - if (options.$testThrowPhase2) throw options.$testThrowPhase2; - const thePool = new OpfsSAHPool(options); - return thePool.isReady.then(async () => { - /** The poolUtil object will be the result of the - resolved Promise. */ - const poolUtil = new OpfsSAHPoolUtil(thePool); - if (sqlite3.oo1) { - const oo1 = sqlite3.oo1; - const theVfs = thePool.getVfs(); - const OpfsSAHPoolDb = function(...args) { - const opt = oo1.DB.dbCtorHelper.normalizeArgs(...args); - opt.vfs = theVfs.$zName; - oo1.DB.dbCtorHelper.call(this, opt); - }; - OpfsSAHPoolDb.prototype = Object.create(oo1.DB.prototype); - poolUtil.OpfsSAHPoolDb = OpfsSAHPoolDb; - } - thePool.log("VFS initialized."); - return poolUtil; - }).catch(async (e) => { - await thePool.removeVfs().catch(() => {}); - throw e; - }); - }).catch((err) => { - return initPromises[vfsName] = Promise.reject(err); - }); - }; - }); - try { - const bootstrapConfig = Object.assign( - Object.create(null), - /** The WASM-environment-dependent configuration for sqlite3ApiBootstrap() */ - { - memory: "undefined" !== typeof wasmMemory ? wasmMemory : EmscriptenModule["wasmMemory"], - exports: "undefined" !== typeof wasmExports ? wasmExports : Object.prototype.hasOwnProperty.call(EmscriptenModule, "wasmExports") ? EmscriptenModule["wasmExports"] : EmscriptenModule["asm"] - }, - globalThis.sqlite3ApiBootstrap.defaultConfig, - globalThis.sqlite3ApiConfig || {} - ); - sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig); - /** - For purposes of the Emscripten build, call sqlite3ApiBootstrap(). - Ideally clients should be able to inject their own config here, - but that's not practical in this particular build constellation - because of the order everything happens in. Clients may either - define globalThis.sqlite3ApiConfig or modify - globalThis.sqlite3ApiBootstrap.defaultConfig to tweak the default - configuration used by a no-args call to sqlite3ApiBootstrap(), - but must have first loaded their WASM module in order to be able - to provide the necessary configuration state. - */ - const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig); - delete globalThis.sqlite3ApiBootstrap; - return p; - } catch (e) { - console.error("sqlite3ApiBootstrap() error:", e); - throw e; - } - }; - if (runtimeInitialized) moduleRtn = Module; - else moduleRtn = new Promise((resolve, reject) => { - readyPromiseResolve = resolve; - readyPromiseReject = reject; - }); - return moduleRtn; -} -sqlite3InitModule = (function() { - /** - In order to hide the sqlite3InitModule()'s resulting - Emscripten module from downstream clients (and simplify our - documentation by being able to elide those details), we hide that - function and expose a hand-written sqlite3InitModule() to return - the sqlite3 object (most of the time). - */ - const originalInit = sqlite3InitModule; - if (!originalInit) throw new Error("Expecting sqlite3InitModule to be defined by the Emscripten build."); - /** - We need to add some state which our custom Module.locateFile() - can see, but an Emscripten limitation currently prevents us from - attaching it to the sqlite3InitModule function object: - - https://github.com/emscripten-core/emscripten/issues/18071 - - The only(?) current workaround is to temporarily stash this state - into the global scope and delete it when sqlite3InitModule() - is called. - */ - const sIMS = globalThis.sqlite3InitModuleState = Object.assign(Object.create(null), { - moduleScript: globalThis?.document?.currentScript, - isWorker: "undefined" !== typeof WorkerGlobalScope, - location: globalThis.location, - urlParams: globalThis?.location?.href ? new URL(globalThis.location.href).searchParams : new URLSearchParams(), - wasmFilename: "sqlite3.wasm" - }); - sIMS.debugModule = sIMS.urlParams.has("sqlite3.debugModule") ? (...args) => console.warn("sqlite3.debugModule:", ...args) : () => {}; - if (sIMS.urlParams.has("sqlite3.dir")) sIMS.sqlite3Dir = sIMS.urlParams.get("sqlite3.dir") + "/"; - else if (sIMS.moduleScript) { - const li = sIMS.moduleScript.src.split("/"); - li.pop(); - sIMS.sqlite3Dir = li.join("/") + "/"; - } - const sIM = globalThis.sqlite3InitModule = function ff(...args) { - sIMS.emscriptenLocateFile = args[0]?.locateFile; - sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm; - return originalInit(...args).then((EmscriptenModule) => { - sIMS.debugModule("sqlite3InitModule() sIMS =", sIMS); - sIMS.debugModule("sqlite3InitModule() EmscriptenModule =", EmscriptenModule); - const s = EmscriptenModule.runSQLite3PostLoadInit(sIMS, EmscriptenModule, !!ff.__isUnderTest); - sIMS.debugModule("sqlite3InitModule() sqlite3 =", s); - return s; - }).catch((e) => { - console.error("Exception loading sqlite3 module:", e); - throw e; - }); - }; - sIM.ready = originalInit.ready; - if (sIMS.moduleScript) { - let src = sIMS.moduleScript.src.split("/"); - src.pop(); - sIMS.scriptDir = src.join("/") + "/"; - } - sIMS.debugModule("extern-post-js.c-pp.js sqlite3InitModuleState =", sIMS); - return sIM; -})(); -var sqlite3_bundler_friendly_default = sqlite3InitModule; -//#endregion -export { sqlite3_bundler_friendly_default as default, sqlite3_worker1_promiser_default as sqlite3Worker1Promiser }; diff --git a/src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js b/src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js deleted file mode 100644 index 57a8feb7..00000000 --- a/src/web/sqlite-wasm/sqlite3-opfs-async-proxy.js +++ /dev/null @@ -1,666 +0,0 @@ -"use strict"; -(function() { - //#region src/bin/sqlite3-opfs-async-proxy.js - /* @preserve - 2022-09-16 - - The author disclaims copyright to this source code. In place of a - legal notice, here is a blessing: - - * May you do good and not evil. - * May you find forgiveness for yourself and forgive others. - * May you share freely, never taking more than you give. - - *********************************************************************** - - A Worker which manages asynchronous OPFS handles on behalf of a - synchronous API which controls it via a combination of Worker - messages, SharedArrayBuffer, and Atomics. It is the asynchronous - counterpart of the API defined in sqlite3-vfs-opfs.js. - - Highly indebted to: - - https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/OriginPrivateFileSystemVFS.js - - for demonstrating how to use the OPFS APIs. - - This file is to be loaded as a Worker. It does not have any direct - access to the sqlite3 JS/WASM bits, so any bits which it needs (most - notably SQLITE_xxx integer codes) have to be imported into it via an - initialization process. - - This file represents an implementation detail of a larger piece of - code, and not a public interface. Its details may change at any time - and are not intended to be used by any client-level code. - - 2022-11-27: Chrome v108 changes some async methods to synchronous, as - documented at: - - https://developer.chrome.com/blog/sync-methods-for-accesshandles/ - - Firefox v111 and Safari 16.4, both released in March 2023, also - include this. - - We cannot change to the sync forms at this point without breaking - clients who use Chrome v104-ish or higher. truncate(), getSize(), - flush(), and close() are now (as of v108) synchronous. Calling them - with an "await", as we have to for the async forms, is still legal - with the sync forms but is superfluous. Calling the async forms with - theFunc().then(...) is not compatible with the change to - synchronous, but we do do not use those APIs that way. i.e. we don't - _need_ to change anything for this, but at some point (after Chrome - versions (approximately) 104-107 are extinct) should change our - usage of those methods to remove the "await". - */ - const wPost = (type, ...args) => postMessage({ - type, - payload: args - }); - const installAsyncProxy = function() { - const toss = function(...args) { - throw new Error(args.join(" ")); - }; - if (globalThis.window === globalThis) toss("This code cannot run from the main thread.", "Load it as a Worker from a separate Worker."); - else if (!navigator?.storage?.getDirectory) toss("This API requires navigator.storage.getDirectory."); - /** - Will hold state copied to this object from the synchronous side of - this API. - */ - const state = Object.create(null); - /** - verbose: - - 0 = no logging output - 1 = only errors - 2 = warnings and errors - 3 = debug, warnings, and errors - */ - state.verbose = 1; - const loggers = { - 0: console.error.bind(console), - 1: console.warn.bind(console), - 2: console.log.bind(console) - }; - const logImpl = (level, ...args) => { - if (state.verbose > level) loggers[level]("OPFS asyncer:", ...args); - }; - const log = (...args) => logImpl(2, ...args); - const warn = (...args) => logImpl(1, ...args); - const error = (...args) => logImpl(0, ...args); - /** - __openFiles is a map of sqlite3_file pointers (integers) to - metadata related to a given OPFS file handles. The pointers are, in - this side of the interface, opaque file handle IDs provided by the - synchronous part of this constellation. Each value is an object - with a structure demonstrated in the xOpen() impl. - */ - const __openFiles = Object.create(null); - /** - __implicitLocks is a Set of sqlite3_file pointers (integers) which were - "auto-locked". i.e. those for which we obtained a sync access - handle without an explicit xLock() call. Such locks will be - released during db connection idle time, whereas a sync access - handle obtained via xLock(), or subsequently xLock()'d after - auto-acquisition, will not be released until xUnlock() is called. - - Maintenance reminder: if we relinquish auto-locks at the end of the - operation which acquires them, we pay a massive performance - penalty: speedtest1 benchmarks take up to 4x as long. By delaying - the lock release until idle time, the hit is negligible. - */ - const __implicitLocks = /* @__PURE__ */ new Set(); - /** - Expects an OPFS file path. It gets resolved, such that ".." - components are properly expanded, and returned. If the 2nd arg is - true, the result is returned as an array of path elements, else an - absolute path string is returned. - */ - const getResolvedPath = function(filename, splitIt) { - const p = new URL(filename, "file://irrelevant").pathname; - return splitIt ? p.split("/").filter((v) => !!v) : p; - }; - /** - Takes the absolute path to a filesystem element. Returns an array - of [handleOfContainingDir, filename]. If the 2nd argument is truthy - then each directory element leading to the file is created along - the way. Throws if any creation or resolution fails. - */ - const getDirForFilename = async function f(absFilename, createDirs = false) { - const path = getResolvedPath(absFilename, true); - const filename = path.pop(); - let dh = state.rootDir; - for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); - return [dh, filename]; - }; - /** - If the given file-holding object has a sync handle attached to it, - that handle is removed and asynchronously closed. Though it may - sound sensible to continue work as soon as the close() returns - (noting that it's asynchronous), doing so can cause operations - performed soon afterwards, e.g. a call to getSyncHandle(), to fail - because they may happen out of order from the close(). OPFS does - not guaranty that the actual order of operations is retained in - such cases. i.e. always "await" on the result of this function. - */ - const closeSyncHandle = async (fh) => { - if (fh.syncHandle) { - log("Closing sync handle for", fh.filenameAbs); - const h = fh.syncHandle; - delete fh.syncHandle; - delete fh.xLock; - __implicitLocks.delete(fh.fid); - return h.close(); - } - }; - /** - A proxy for closeSyncHandle() which is guaranteed to not throw. - - This function is part of a lock/unlock step in functions which - require a sync access handle but may be called without xLock() - having been called first. Such calls need to release that - handle to avoid locking the file for all of time. This is an - _attempt_ at reducing cross-tab contention but it may prove - to be more of a problem than a solution and may need to be - removed. - */ - const closeSyncHandleNoThrow = async (fh) => { - try { - await closeSyncHandle(fh); - } catch (e) { - warn("closeSyncHandleNoThrow() ignoring:", e, fh); - } - }; - const releaseImplicitLocks = async () => { - if (__implicitLocks.size) for (const fid of __implicitLocks) { - const fh = __openFiles[fid]; - await closeSyncHandleNoThrow(fh); - log("Auto-unlocked", fid, fh.filenameAbs); - } - }; - /** - An experiment in improving concurrency by freeing up implicit locks - sooner. This is known to impact performance dramatically but it has - also shown to improve concurrency considerably. - - If fh.releaseImplicitLocks is truthy and fh is in __implicitLocks, - this routine returns closeSyncHandleNoThrow(), else it is a no-op. - */ - const releaseImplicitLock = async (fh) => { - if (fh.releaseImplicitLocks && __implicitLocks.has(fh.fid)) return closeSyncHandleNoThrow(fh); - }; - /** - An error class specifically for use with getSyncHandle(), the goal - of which is to eventually be able to distinguish unambiguously - between locking-related failures and other types, noting that we - cannot currently do so because createSyncAccessHandle() does not - define its exceptions in the required level of detail. - - 2022-11-29: according to: - - https://github.com/whatwg/fs/pull/21 - - NoModificationAllowedError will be the standard exception thrown - when acquisition of a sync access handle fails due to a locking - error. As of this writing, that error type is not visible in the - dev console in Chrome v109, nor is it documented in MDN, but an - error with that "name" property is being thrown from the OPFS - layer. - */ - class GetSyncHandleError extends Error { - constructor(errorObject, ...msg) { - super([ - ...msg, - ": " + errorObject.name + ":", - errorObject.message - ].join(" "), { cause: errorObject }); - this.name = "GetSyncHandleError"; - } - } - /** - Attempts to find a suitable SQLITE_xyz result code for Error - object e. Returns either such a translation or rc if if it does - not know how to translate the exception. - */ - GetSyncHandleError.convertRc = (e, rc) => { - if (e instanceof GetSyncHandleError) { - if (e.cause.name === "NoModificationAllowedError" || e.cause.name === "DOMException" && 0 === e.cause.message.indexOf("Access Handles cannot")) return state.sq3Codes.SQLITE_BUSY; - else if ("NotFoundError" === e.cause.name) - /** - Maintenance reminder: SQLITE_NOTFOUND, though it looks like - a good match, has different semantics than NotFoundError - and is not suitable here. - */ - return state.sq3Codes.SQLITE_CANTOPEN; - } else if ("NotFoundError" === e?.name) return state.sq3Codes.SQLITE_CANTOPEN; - return rc; - }; - /** - Returns the sync access handle associated with the given file - handle object (which must be a valid handle object, as created by - xOpen()), lazily opening it if needed. - - In order to help alleviate cross-tab contention for a dabase, if - an exception is thrown while acquiring the handle, this routine - will wait briefly and try again, up to some fixed number of - times. If acquisition still fails at that point it will give up - and propagate the exception. Client-level code will see that as - an I/O error. - - 2024-06-12: there is a rare race condition here which has been - reported a single time: - - https://sqlite.org/forum/forumpost/9ee7f5340802d600 - - What appears to be happening is that file we're waiting for a - lock on is deleted while we wait. What currently happens here is - that a locking exception is thrown but the exception type is - NotFoundError. In such cases, we very probably should attempt to - re-open/re-create the file an obtain the lock on it (noting that - there's another race condition there). That's easy to say but - creating a viable test for that condition has proven challenging - so far. - */ - const getSyncHandle = async (fh, opName) => { - if (!fh.syncHandle) { - const t = performance.now(); - log("Acquiring sync handle for", fh.filenameAbs); - const maxTries = 6, msBase = state.asyncIdleWaitTime * 2; - let i = 1, ms = msBase; - for (;; ms = msBase * ++i) try { - fh.syncHandle = await fh.fileHandle.createSyncAccessHandle(); - break; - } catch (e) { - if (i === maxTries) throw new GetSyncHandleError(e, "Error getting sync handle for", opName + "().", maxTries, "attempts failed.", fh.filenameAbs); - warn("Error getting sync handle for", opName + "(). Waiting", ms, "ms and trying again.", fh.filenameAbs, e); - Atomics.wait(state.sabOPView, state.opIds.retry, 0, ms); - } - log("Got", opName + "() sync handle for", fh.filenameAbs, "in", performance.now() - t, "ms"); - if (!fh.xLock) { - __implicitLocks.add(fh.fid); - log("Acquired implicit lock for", opName + "()", fh.fid, fh.filenameAbs); - } - } - return fh.syncHandle; - }; - /** - Stores the given value at state.sabOPView[state.opIds.rc] and then - Atomics.notify()'s it. - */ - const storeAndNotify = (opName, value) => { - log(opName + "() => notify(", value, ")"); - Atomics.store(state.sabOPView, state.opIds.rc, value); - Atomics.notify(state.sabOPView, state.opIds.rc); - }; - /** - Throws if fh is a file-holding object which is flagged as read-only. - */ - const affirmNotRO = function(opName, fh) { - if (fh.readOnly) toss(opName + "(): File is read-only: " + fh.filenameAbs); - }; - /** - Gets set to true by the 'opfs-async-shutdown' command to quit the - wait loop. This is only intended for debugging purposes: we cannot - inspect this file's state while the tight waitLoop() is running and - need a way to stop that loop for introspection purposes. - */ - let flagAsyncShutdown = false; - /** - Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods - methods, as well as helpers like mkdir(). - */ - const vfsAsyncImpls = { - "opfs-async-shutdown": async () => { - flagAsyncShutdown = true; - storeAndNotify("opfs-async-shutdown", 0); - }, - mkdir: async (dirname) => { - let rc = 0; - try { - await getDirForFilename(dirname + "/filepart", true); - } catch (e) { - state.s11n.storeException(2, e); - rc = state.sq3Codes.SQLITE_IOERR; - } - storeAndNotify("mkdir", rc); - }, - xAccess: async (filename) => { - let rc = 0; - try { - const [dh, fn] = await getDirForFilename(filename); - await dh.getFileHandle(fn); - } catch (e) { - state.s11n.storeException(2, e); - rc = state.sq3Codes.SQLITE_IOERR; - } - storeAndNotify("xAccess", rc); - }, - xClose: async function(fid) { - const opName = "xClose"; - __implicitLocks.delete(fid); - const fh = __openFiles[fid]; - let rc = 0; - if (fh) { - delete __openFiles[fid]; - await closeSyncHandle(fh); - if (fh.deleteOnClose) try { - await fh.dirHandle.removeEntry(fh.filenamePart); - } catch (e) { - warn("Ignoring dirHandle.removeEntry() failure of", fh, e); - } - } else { - state.s11n.serialize(); - rc = state.sq3Codes.SQLITE_NOTFOUND; - } - storeAndNotify(opName, rc); - }, - xDelete: async function(...args) { - storeAndNotify("xDelete", await vfsAsyncImpls.xDeleteNoWait(...args)); - }, - xDeleteNoWait: async function(filename, syncDir = 0, recursive = false) { - let rc = 0; - try { - while (filename) { - const [hDir, filenamePart] = await getDirForFilename(filename, false); - if (!filenamePart) break; - await hDir.removeEntry(filenamePart, { recursive }); - if (4660 !== syncDir) break; - recursive = false; - filename = getResolvedPath(filename, true); - filename.pop(); - filename = filename.join("/"); - } - } catch (e) { - state.s11n.storeException(2, e); - rc = state.sq3Codes.SQLITE_IOERR_DELETE; - } - return rc; - }, - xFileSize: async function(fid) { - const fh = __openFiles[fid]; - let rc = 0; - try { - const sz = await (await getSyncHandle(fh, "xFileSize")).getSize(); - state.s11n.serialize(Number(sz)); - } catch (e) { - state.s11n.storeException(1, e); - rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR); - } - await releaseImplicitLock(fh); - storeAndNotify("xFileSize", rc); - }, - xLock: async function(fid, lockType) { - const fh = __openFiles[fid]; - let rc = 0; - const oldLockType = fh.xLock; - fh.xLock = lockType; - if (!fh.syncHandle) try { - await getSyncHandle(fh, "xLock"); - __implicitLocks.delete(fid); - } catch (e) { - state.s11n.storeException(1, e); - rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_LOCK); - fh.xLock = oldLockType; - } - storeAndNotify("xLock", rc); - }, - xOpen: async function(fid, filename, flags, opfsFlags) { - const opName = "xOpen"; - const create = state.sq3Codes.SQLITE_OPEN_CREATE & flags; - try { - let hDir, filenamePart; - try { - [hDir, filenamePart] = await getDirForFilename(filename, !!create); - } catch (e) { - state.s11n.storeException(1, e); - storeAndNotify(opName, state.sq3Codes.SQLITE_NOTFOUND); - return; - } - if (state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN & opfsFlags) try { - await hDir.removeEntry(filenamePart); - } catch (e) {} - const hFile = await hDir.getFileHandle(filenamePart, { create }); - const fh = Object.assign(Object.create(null), { - fid, - filenameAbs: filename, - filenamePart, - dirHandle: hDir, - fileHandle: hFile, - sabView: state.sabFileBufView, - readOnly: !create && !!(state.sq3Codes.SQLITE_OPEN_READONLY & flags), - deleteOnClose: !!(state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags) - }); - fh.releaseImplicitLocks = opfsFlags & state.opfsFlags.OPFS_UNLOCK_ASAP || state.opfsFlags.defaultUnlockAsap; - __openFiles[fid] = fh; - storeAndNotify(opName, 0); - } catch (e) { - error(opName, e); - state.s11n.storeException(1, e); - storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR); - } - }, - xRead: async function(fid, n, offset64) { - let rc = 0, nRead; - const fh = __openFiles[fid]; - try { - nRead = (await getSyncHandle(fh, "xRead")).read(fh.sabView.subarray(0, n), { at: Number(offset64) }); - if (nRead < n) { - fh.sabView.fill(0, nRead, n); - rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; - } - } catch (e) { - error("xRead() failed", e, fh); - state.s11n.storeException(1, e); - rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_READ); - } - await releaseImplicitLock(fh); - storeAndNotify("xRead", rc); - }, - xSync: async function(fid, flags) { - const fh = __openFiles[fid]; - let rc = 0; - if (!fh.readOnly && fh.syncHandle) try { - await fh.syncHandle.flush(); - } catch (e) { - state.s11n.storeException(2, e); - rc = state.sq3Codes.SQLITE_IOERR_FSYNC; - } - storeAndNotify("xSync", rc); - }, - xTruncate: async function(fid, size) { - let rc = 0; - const fh = __openFiles[fid]; - try { - affirmNotRO("xTruncate", fh); - await (await getSyncHandle(fh, "xTruncate")).truncate(size); - } catch (e) { - error("xTruncate():", e, fh); - state.s11n.storeException(2, e); - rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_TRUNCATE); - } - await releaseImplicitLock(fh); - storeAndNotify("xTruncate", rc); - }, - xUnlock: async function(fid, lockType) { - let rc = 0; - const fh = __openFiles[fid]; - if (fh.syncHandle && state.sq3Codes.SQLITE_LOCK_NONE === lockType) try { - await closeSyncHandle(fh); - } catch (e) { - state.s11n.storeException(1, e); - rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; - } - storeAndNotify("xUnlock", rc); - }, - xWrite: async function(fid, n, offset64) { - let rc; - const fh = __openFiles[fid]; - try { - affirmNotRO("xWrite", fh); - rc = n === (await getSyncHandle(fh, "xWrite")).write(fh.sabView.subarray(0, n), { at: Number(offset64) }) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; - } catch (e) { - error("xWrite():", e, fh); - state.s11n.storeException(1, e); - rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_WRITE); - } - await releaseImplicitLock(fh); - storeAndNotify("xWrite", rc); - } - }; - const initS11n = () => { - /** - ACHTUNG: this code is 100% duplicated in the other half of this - proxy! The documentation is maintained in the "synchronous half". - */ - if (state.s11n) return state.s11n; - const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - state.s11n = Object.create(null); - const TypeIds = Object.create(null); - TypeIds.number = { - id: 1, - size: 8, - getter: "getFloat64", - setter: "setFloat64" - }; - TypeIds.bigint = { - id: 2, - size: 8, - getter: "getBigInt64", - setter: "setBigInt64" - }; - TypeIds.boolean = { - id: 3, - size: 4, - getter: "getInt32", - setter: "setInt32" - }; - TypeIds.string = { id: 4 }; - const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v); - const getTypeIdById = (tid) => { - switch (tid) { - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:", tid); - } - }; - state.s11n.deserialize = function(clear = false) { - const argc = viewU8[0]; - const rc = argc ? [] : null; - if (argc) { - const typeIds = []; - let offset = 1, i, n, v; - for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset])); - for (i = 0; i < argc; ++i) { - const t = typeIds[i]; - if (t.getter) { - v = viewDV[t.getter](offset, state.littleEndian); - offset += t.size; - } else { - n = viewDV.getInt32(offset, state.littleEndian); - offset += 4; - v = textDecoder.decode(viewU8.slice(offset, offset + n)); - offset += n; - } - rc.push(v); - } - } - if (clear) viewU8[0] = 0; - return rc; - }; - state.s11n.serialize = function(...args) { - if (args.length) { - const typeIds = []; - let i = 0, offset = 1; - viewU8[0] = args.length & 255; - for (; i < args.length; ++i, ++offset) { - typeIds.push(getTypeId(args[i])); - viewU8[offset] = typeIds[i].id; - } - for (i = 0; i < args.length; ++i) { - const t = typeIds[i]; - if (t.setter) { - viewDV[t.setter](offset, args[i], state.littleEndian); - offset += t.size; - } else { - const s = textEncoder.encode(args[i]); - viewDV.setInt32(offset, s.byteLength, state.littleEndian); - offset += 4; - viewU8.set(s, offset); - offset += s.byteLength; - } - } - } else viewU8[0] = 0; - }; - state.s11n.storeException = state.asyncS11nExceptions ? ((priority, e) => { - if (priority <= state.asyncS11nExceptions) state.s11n.serialize([ - e.name, - ": ", - e.message - ].join("")); - }) : () => {}; - return state.s11n; - }; - const waitLoop = async function f() { - const opHandlers = Object.create(null); - for (let k of Object.keys(state.opIds)) { - const vi = vfsAsyncImpls[k]; - if (!vi) continue; - const o = Object.create(null); - opHandlers[state.opIds[k]] = o; - o.key = k; - o.f = vi; - } - while (!flagAsyncShutdown) try { - if ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.whichOp, 0, state.asyncIdleWaitTime)) { - await releaseImplicitLocks(); - continue; - } - const opId = Atomics.load(state.sabOPView, state.opIds.whichOp); - Atomics.store(state.sabOPView, state.opIds.whichOp, 0); - const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #", opId); - const args = state.s11n.deserialize(true) || []; - if (hnd.f) await hnd.f(...args); - else error("Missing callback for opId", opId); - } catch (e) { - error("in waitLoop():", e); - } - }; - navigator.storage.getDirectory().then(function(d) { - state.rootDir = d; - globalThis.onmessage = function({ data }) { - switch (data.type) { - case "opfs-async-init": { - const opt = data.args; - for (const k in opt) state[k] = opt[k]; - state.verbose = opt.verbose ?? 1; - state.sabOPView = new Int32Array(state.sabOP); - state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); - state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - Object.keys(vfsAsyncImpls).forEach((k) => { - if (!Number.isFinite(state.opIds[k])) toss("Maintenance required: missing state.opIds[", k, "]"); - }); - initS11n(); - log("init state", state); - wPost("opfs-async-inited"); - waitLoop(); - break; - } - case "opfs-async-restart": - if (flagAsyncShutdown) { - warn("Restarting after opfs-async-shutdown. Might or might not work."); - flagAsyncShutdown = false; - waitLoop(); - } - break; - } - }; - wPost("opfs-async-loaded"); - }).catch((e) => error("error initializing OPFS asyncer:", e)); - }; - if (!globalThis.SharedArrayBuffer) wPost("opfs-unavailable", "Missing SharedArrayBuffer API.", "The server must emit the COOP/COEP response headers to enable that."); - else if (!globalThis.Atomics) wPost("opfs-unavailable", "Missing Atomics API.", "The server must emit the COOP/COEP response headers to enable that."); - else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) wPost("opfs-unavailable", "Missing required OPFS APIs."); - else installAsyncProxy(); - //#endregion -})(); diff --git a/src/web/sqlite-wasm/sqlite3-worker1.mjs b/src/web/sqlite-wasm/sqlite3-worker1.mjs deleted file mode 100644 index 533b4e9a..00000000 --- a/src/web/sqlite-wasm/sqlite3-worker1.mjs +++ /dev/null @@ -1,15461 +0,0 @@ -//#region src/bin/sqlite3.mjs -/* @preserve -** -** LICENSE for the sqlite3 WebAssembly/JavaScript APIs. -** -** This bundle (typically released as sqlite3.js or sqlite3.mjs) -** is an amalgamation of JavaScript source code from two projects: -** -** 1) https://emscripten.org: the Emscripten "glue code" is covered by -** the terms of the MIT license and University of Illinois/NCSA -** Open Source License, as described at: -** -** https://emscripten.org/docs/introducing_emscripten/emscripten_license.html -** -** 2) https://sqlite.org: all code and documentation labeled as being -** from this source are released under the same terms as the sqlite3 -** C library: -** -** 2022-10-16 -** -** The author disclaims copyright to this source code. In place of a -** legal notice, here is a blessing: -** -** * May you do good and not evil. -** * May you find forgiveness for yourself and forgive others. -** * May you share freely, never taking more than you give. -*/ -/* @preserve -** This code was built from sqlite3 version... -** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" -** -** Emscripten SDK: 5.0.0 -*/ -async function sqlite3InitModule(moduleArg = {}) { - var moduleRtn; - var Module = moduleArg; - var ENVIRONMENT_IS_WEB = !!globalThis.window; - var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; - globalThis.process?.versions?.node && globalThis.process?.type; - /** - BEGIN FILE: api/pre-js.js - - This file is intended to be prepended to the sqlite3.js build using - Emscripten's --pre-js=THIS_FILE flag (or equivalent). It is run - from inside of sqlite3InitModule(), after Emscripten's Module is - defined, but early enough that we can ammend, or even outright - replace, Module from here. - - Because this runs in-between Emscripten's own bootstrapping and - Emscripten's main work, we must be careful with file-local symbol - names. e.g. don't overwrite anything Emscripten defines and do not - use 'const' for local symbols which Emscripten might try to use for - itself. i.e. try to keep file-local symbol names obnoxiously - collision-resistant. - */ - /** - This file was preprocessed using: - - ./c-pp-lite -o ./bld/pre-js.esm.js -Dtarget:es6-module -DModule.instantiateWasm api/pre-js.c-pp.js - */ - (function(Module) { - const sIMS = globalThis.sqlite3InitModuleState || Object.assign(Object.create(null), { debugModule: function() { - console.warn("globalThis.sqlite3InitModuleState is missing", arguments); - } }); - delete globalThis.sqlite3InitModuleState; - sIMS.debugModule("pre-js.js sqlite3InitModuleState =", sIMS); - /** - This custom locateFile() tries to figure out where to load `path` - from. The intent is to provide a way for foo/bar/X.js loaded from a - Worker constructor or importScripts() to be able to resolve - foo/bar/X.wasm (in the latter case, with some help): - - 1) If URL param named the same as `path` is set, it is returned. - - 2) If sqlite3InitModuleState.sqlite3Dir is set, then (thatName + path) - is returned (it's assumed to end with '/'). - - 3) If this code is running in the main UI thread AND it was loaded - from a SCRIPT tag, the directory part of that URL is used - as the prefix. (This form of resolution unfortunately does not - function for scripts loaded via importScripts().) - - 4) If none of the above apply, (prefix+path) is returned. - - None of the above apply in ES6 builds, which uses a much simpler - approach. - */ - Module["locateFile"] = function(path, prefix) { - if (this.emscriptenLocateFile instanceof Function) return this.emscriptenLocateFile(path, prefix); - return new URL(path, import.meta.url).href; - }.bind(sIMS); - /** - Override Module.instantiateWasm(). - - A custom Module.instantiateWasm() does not work in WASMFS builds: - - https://github.com/emscripten-core/emscripten/issues/17951 - - In such builds we must disable this. - - It's disabled in the (unsupported/untested) node builds because - node does not do fetch(). - */ - Module["instantiateWasm"] = function callee(imports, onSuccess) { - if (this.emscriptenInstantiateWasm instanceof Function) return this.emscriptenInstantiateWasm(imports, onSuccess); - const sims = this; - const uri = Module.locateFile(sims.wasmFilename, "undefined" === typeof scriptDirectory ? "" : scriptDirectory); - sims.debugModule("instantiateWasm() uri =", uri, "sIMS =", this); - const wfetch = () => fetch(uri, { credentials: "same-origin" }); - const finalThen = (arg) => { - arg.imports = imports; - sims.instantiateWasm = arg; - onSuccess(arg.instance, arg.module); - }; - return (WebAssembly.instantiateStreaming ? async () => WebAssembly.instantiateStreaming(wfetch(), imports).then(finalThen) : async () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, imports)).then(finalThen))(); - }.bind(sIMS); - })(Module); - var thisProgram = "./this.program"; - var _scriptName = import.meta.url; - 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) { - try { - scriptDirectory = new URL(".", _scriptName).href; - } catch {} - 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 = async (url) => { - var response = await fetch(url, { credentials: "same-origin" }); - if (response.ok) return response.arrayBuffer(); - throw new Error(response.status + " : " + response.url); - }; - } - var out = console.log.bind(console); - var err = console.error.bind(console); - var wasmBinary; - var ABORT = false, readyPromiseResolve, readyPromiseReject, HEAP8, HEAPU8, HEAP16, HEAP32, HEAPU32, HEAP64; - var runtimeInitialized = false; - function updateMemoryViews() { - var b = wasmMemory.buffer; - HEAP8 = new Int8Array(b); - HEAP16 = new Int16Array(b); - HEAPU8 = new Uint8Array(b); - new Uint16Array(b); - HEAP32 = new Int32Array(b); - HEAPU32 = new Uint32Array(b); - new Float32Array(b); - new Float64Array(b); - HEAP64 = new BigInt64Array(b); - new BigUint64Array(b); - } - function initMemory() { - if (Module["wasmMemory"]) wasmMemory = Module["wasmMemory"]; - else { - var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 8388608; - /** @suppress {checkTypes} */ - wasmMemory = new WebAssembly.Memory({ - "initial": INITIAL_MEMORY / 65536, - "maximum": 32768 - }); - } - updateMemoryViews(); - } - function preRun() { - if (Module["preRun"]) { - if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; - while (Module["preRun"].length) addOnPreRun(Module["preRun"].shift()); - } - callRuntimeCallbacks(onPreRuns); - } - function initRuntime() { - runtimeInitialized = true; - if (!Module["noFSInit"] && !FS.initialized) FS.init(); - TTY.init(); - wasmExports["__wasm_call_ctors"](); - FS.ignorePermissions = false; - } - function postRun() { - if (Module["postRun"]) { - if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; - while (Module["postRun"].length) addOnPostRun(Module["postRun"].shift()); - } - callRuntimeCallbacks(onPostRuns); - } - /** @param {string|number=} what */ - function abort(what) { - Module["onAbort"]?.(what); - what = "Aborted(" + what + ")"; - err(what); - ABORT = true; - what += ". Build with -sASSERTIONS for more info."; - /** @suppress {checkTypes} */ - var e = new WebAssembly.RuntimeError(what); - readyPromiseReject?.(e); - throw e; - } - var wasmBinaryFile; - function findWasmBinary() { - if (Module["locateFile"]) return locateFile("sqlite3.wasm"); - return new URL("sqlite3.wasm", import.meta.url).href; - } - 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"; - } - async function getWasmBinary(binaryFile) { - if (!wasmBinary) try { - var response = await readAsync(binaryFile); - return new Uint8Array(response); - } catch {} - return getBinarySync(binaryFile); - } - async function instantiateArrayBuffer(binaryFile, imports) { - try { - var binary = await getWasmBinary(binaryFile); - return await WebAssembly.instantiate(binary, imports); - } catch (reason) { - err(`failed to asynchronously prepare wasm: ${reason}`); - abort(reason); - } - } - async function instantiateAsync(binary, binaryFile, imports) { - if (!binary) try { - var response = fetch(binaryFile, { credentials: "same-origin" }); - return await WebAssembly.instantiateStreaming(response, imports); - } catch (reason) { - err(`wasm streaming compile failed: ${reason}`); - err("falling back to ArrayBuffer instantiation"); - } - return instantiateArrayBuffer(binaryFile, imports); - } - function getWasmImports() { - return { - "env": wasmImports, - "wasi_snapshot_preview1": wasmImports - }; - } - async function createWasm() { - /** @param {WebAssembly.Module=} module*/ - function receiveInstance(instance, module) { - wasmExports = instance.exports; - assignWasmExports(wasmExports); - return wasmExports; - } - function receiveInstantiationResult(result) { - return receiveInstance(result["instance"]); - } - var info = getWasmImports(); - if (Module["instantiateWasm"]) return new Promise((resolve, reject) => { - Module["instantiateWasm"](info, (inst, mod) => { - resolve(receiveInstance(inst, mod)); - }); - }); - wasmBinaryFile ??= findWasmBinary(); - return receiveInstantiationResult(await instantiateAsync(wasmBinary, wasmBinaryFile, info)); - } - var callRuntimeCallbacks = (callbacks) => { - while (callbacks.length > 0) callbacks.shift()(Module); - }; - var onPostRuns = []; - var addOnPostRun = (cb) => onPostRuns.push(cb); - var onPreRuns = []; - var addOnPreRun = (cb) => onPreRuns.push(cb); - var wasmMemory; - var PATH = { - isAbs: (path) => path.charAt(0) === "/", - splitPath: (filename) => { - return /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.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.slice(-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.slice(0, -1); - return root + dir; - }, - basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1], - join: (...paths) => PATH.normalize(paths.join("/")), - join2: (l, r) => PATH.normalize(l + "/" + r) - }; - var initRandomFill = () => { - return (view) => crypto.getRandomValues(view); - }; - 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).slice(1); - to = PATH_FS.resolve(to).slice(1); - function trim(arr) { - var start = 0; - for (; start < arr.length; start++) if (arr[start] !== "") break; - var end = arr.length - 1; - for (; end >= 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 < length; i++) if (fromParts[i] !== toParts[i]) { - samePartsLength = i; - break; - } - var outputParts = []; - for (var i = samePartsLength; i < fromParts.length; i++) outputParts.push(".."); - outputParts = outputParts.concat(toParts.slice(samePartsLength)); - return outputParts.join("/"); - } - }; - var UTF8Decoder = new TextDecoder(); - var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { - var maxIdx = idx + maxBytesToRead; - if (ignoreNul) return maxIdx; - while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; - return idx; - }; - /** - * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given - * array that contains uint8 values, returns a copy of that string as a - * Javascript String object. - * heapOrArray is either a regular array, or a JavaScript typed array view. - * @param {number=} idx - * @param {number=} maxBytesToRead - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ - var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { - var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); - return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr))); - }; - var FS_stdin_getChar_buffer = []; - var lengthBytesUTF8 = (str) => { - var len = 0; - for (var i = 0; i < str.length; ++i) { - var c = str.charCodeAt(i); - if (c <= 127) len++; - else if (c <= 2047) len += 2; - else if (c >= 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 < str.length; ++i) { - var u = str.codePointAt(i); - 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; - i++; - } - } - heap[outIdx] = 0; - return outIdx - startIdx; - }; - /** @type {function(string, boolean=, number=)} */ - var 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 (globalThis.window?.prompt) { - result = window.prompt("Input: "); - if (result !== null) result += "\n"; - } - 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; i < length; i++) { - var result; - try { - result = stream.tty.ops.get_char(stream.tty); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); - if (result === null || result === void 0) break; - bytesRead++; - buffer[offset + i] = result; - } - if (bytesRead) stream.node.atime = Date.now(); - return bytesRead; - }, - write(stream, buffer, offset, length, pos) { - if (!stream.tty || !stream.tty.ops.put_char) throw new FS.ErrnoError(60); - try { - for (var i = 0; i < length; i++) stream.tty.ops.put_char(stream.tty, buffer[offset + i]); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (length) stream.node.mtime = stream.node.ctime = Date.now(); - return i; - } - }, - default_tty_ops: { - get_char(tty) { - return FS_stdin_getChar(); - }, - put_char(tty, val) { - if (val === null || val === 10) { - out(UTF8ArrayToString(tty.output)); - tty.output = []; - } else if (val != 0) tty.output.push(val); - }, - fsync(tty) { - if (tty.output?.length > 0) { - out(UTF8ArrayToString(tty.output)); - 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)); - tty.output = []; - } else if (val != 0) tty.output.push(val); - }, - fsync(tty) { - if (tty.output?.length > 0) { - err(UTF8ArrayToString(tty.output)); - tty.output = []; - } - } - } - }; - var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); - var alignMemory = (size, alignment) => { - return Math.ceil(size / alignment) * alignment; - }; - var mmapAlloc = (size) => { - size = alignMemory(size, 65536); - var ptr = _emscripten_builtin_memalign(65536, size); - if (ptr) zeroMemory(ptr, size); - return ptr; - }; - var MEMFS = { - ops_table: null, - mount(mount) { - return MEMFS.createNode(null, "/", 16895, 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, - 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.atime = node.mtime = node.ctime = Date.now(); - if (parent) { - parent.contents[name] = node; - parent.atime = parent.mtime = parent.ctime = node.atime; - } - 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; - newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 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.atime); - attr.mtime = new Date(node.mtime); - attr.ctime = new Date(node.ctime); - attr.blksize = 4096; - attr.blocks = Math.ceil(attr.size / attr.blksize); - return attr; - }, - setattr(node, attr) { - for (const key of [ - "mode", - "atime", - "mtime", - "ctime" - ]) if (attr[key] != null) node[key] = attr[key]; - if (attr.size !== void 0) MEMFS.resizeFileStorage(node, attr.size); - }, - lookup(parent, name) { - if (!MEMFS.doesNotExistError) { - MEMFS.doesNotExistError = new FS.ErrnoError(44); - /** @suppress {checkTypes} */ - MEMFS.doesNotExistError.stack = ""; - } - throw MEMFS.doesNotExistError; - }, - mknod(parent, name, mode, dev) { - return MEMFS.createNode(parent, name, mode, dev); - }, - rename(old_node, new_dir, new_name) { - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) {} - if (new_node) { - if (FS.isDir(old_node.mode)) for (var i in new_node.contents) throw new FS.ErrnoError(55); - FS.hashRemoveNode(new_node); - } - delete old_node.parent.contents[old_node.name]; - new_dir.contents[new_name] = old_node; - old_node.name = new_name; - new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); - }, - unlink(parent, name) { - delete parent.contents[name]; - parent.ctime = parent.mtime = Date.now(); - }, - rmdir(parent, name) { - for (var i in FS.lookupNode(parent, name).contents) throw new FS.ErrnoError(55); - delete parent.contents[name]; - parent.ctime = parent.mtime = Date.now(); - }, - readdir(node) { - return [ - ".", - "..", - ...Object.keys(node.contents) - ]; - }, - symlink(parent, newname, oldpath) { - var node = MEMFS.createNode(parent, newname, 41471, 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; i < size; i++) buffer[offset + i] = contents[position + i]; - return size; - }, - write(stream, buffer, offset, length, position, canOwn) { - if (buffer.buffer === HEAP8.buffer) canOwn = false; - if (!length) return 0; - var node = stream.node; - node.mtime = node.ctime = Date.now(); - if (buffer.subarray && (!node.contents || node.contents.subarray)) { - if (canOwn) { - node.contents = buffer.subarray(offset, offset + length); - node.usedBytes = length; - return length; - } else if (node.usedBytes === 0 && position === 0) { - node.contents = buffer.slice(offset, offset + length); - node.usedBytes = length; - return length; - } else if (position + length <= node.usedBytes) { - node.contents.set(buffer.subarray(offset, offset + length), position); - return length; - } - } - MEMFS.expandFileStorage(node, position + length); - if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); - else for (var i = 0; i < length; i++) node.contents[position + i] = buffer[offset + i]; - node.usedBytes = Math.max(node.usedBytes, position + length); - return length; - }, - llseek(stream, offset, whence) { - var position = offset; - if (whence === 1) position += stream.position; - else if (whence === 2) { - if (FS.isFile(stream.node.mode)) position += stream.node.usedBytes; - } - if (position < 0) throw new FS.ErrnoError(28); - return position; - }, - mmap(stream, length, position, prot, flags) { - if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43); - var ptr; - var allocated; - var contents = stream.node.contents; - if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { - allocated = false; - ptr = contents.byteOffset; - } else { - allocated = true; - ptr = mmapAlloc(length); - if (!ptr) throw new FS.ErrnoError(48); - if (contents) { - if (position > 0 || position + length < contents.length) if (contents.subarray) contents = contents.subarray(position, position + length); - else contents = Array.prototype.slice.call(contents, position, position + length); - HEAP8.set(contents, ptr); - } - } - return { - ptr, - allocated - }; - }, - msync(stream, buffer, offset, length, mmapFlags) { - MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); - return 0; - } - } - }; - var FS_modeStringToFlags = (str) => { - var flags = { - "r": 0, - "r+": 2, - "w": 577, - "w+": 578, - "a": 1089, - "a+": 1090 - }[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 |= 365; - if (canWrite) mode |= 146; - return mode; - }; - var asyncLoad = async (url) => { - var arrayBuffer = await readAsync(url); - return new Uint8Array(arrayBuffer); - }; - var FS_createDataFile = (...args) => FS.createDataFile(...args); - var getUniqueRunDependency = (id) => { - return id; - }; - var runDependencies = 0; - var dependenciesFulfilled = null; - var removeRunDependency = (id) => { - runDependencies--; - Module["monitorRunDependencies"]?.(runDependencies); - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); - } - } - }; - var addRunDependency = (id) => { - runDependencies++; - Module["monitorRunDependencies"]?.(runDependencies); - }; - var preloadPlugins = []; - var FS_handledByPreloadPlugin = async (byteArray, fullname) => { - if (typeof Browser != "undefined") Browser.init(); - for (var plugin of preloadPlugins) if (plugin["canHandle"](fullname)) return plugin["handle"](byteArray, fullname); - return byteArray; - }; - var FS_preloadFile = async (parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish) => { - var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; - var dep = getUniqueRunDependency(`cp ${fullname}`); - addRunDependency(dep); - try { - var byteArray = url; - if (typeof url == "string") byteArray = await asyncLoad(url); - byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); - preFinish?.(); - if (!dontCreateFile) FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); - } finally { - removeRunDependency(dep); - } - }; - var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { - FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror); - }; - var FS = { - root: null, - mounts: [], - devices: {}, - streams: [], - nextInode: 1, - nameTable: null, - currentPath: "/", - initialized: false, - ignorePermissions: true, - filesystems: null, - syncFSRequests: 0, - readFiles: {}, - ErrnoError: class { - name = "ErrnoError"; - constructor(errno) { - this.errno = errno; - } - }, - FSStream: class { - 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 { - node_ops = {}; - stream_ops = {}; - readMode = 365; - writeMode = 146; - mounted = null; - constructor(parent, name, mode, rdev) { - if (!parent) parent = this; - this.parent = parent; - this.mount = parent.mount; - this.id = FS.nextInode++; - this.name = name; - this.mode = mode; - this.rdev = rdev; - this.atime = this.mtime = this.ctime = Date.now(); - } - 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 = {}) { - if (!path) throw new FS.ErrnoError(44); - opts.follow_mount ??= true; - if (!PATH.isAbs(path)) path = FS.cwd() + "/" + path; - linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { - var parts = path.split("/").filter((p) => !!p); - var current = FS.root; - var current_path = "/"; - for (var i = 0; i < parts.length; i++) { - var islast = i === parts.length - 1; - if (islast && opts.parent) break; - if (parts[i] === ".") continue; - if (parts[i] === "..") { - current_path = PATH.dirname(current_path); - if (FS.isRoot(current)) { - path = current_path + "/" + parts.slice(i + 1).join("/"); - nlinks--; - continue linkloop; - } else current = current.parent; - continue; - } - current_path = PATH.join2(current_path, parts[i]); - try { - current = FS.lookupNode(current, parts[i]); - } catch (e) { - if (e?.errno === 44 && islast && opts.noent_okay) return { path: current_path }; - throw e; - } - if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) current = current.mounted.root; - if (FS.isLink(current.mode) && (!islast || opts.follow)) { - if (!current.node_ops.readlink) throw new FS.ErrnoError(52); - var link = current.node_ops.readlink(current); - if (!PATH.isAbs(link)) link = PATH.dirname(current_path) + "/" + link; - path = link + "/" + parts.slice(i + 1).join("/"); - continue linkloop; - } - } - return { - path: current_path, - node: current - }; - } - throw new FS.ErrnoError(32); - }, - 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 < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0; - return (parentid + hash >>> 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) { - if (!FS.isDir(dir.mode)) return 54; - try { - 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 & 576) return 31; - } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); - }, - checkOpExists(op, err) { - if (!op) throw new FS.ErrnoError(err); - return op; - }, - 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; - }, - doSetAttr(stream, node, attr) { - var setattr = stream?.stream_ops.setattr; - var arg = setattr ? stream : node; - setattr ??= node.node_ops.setattr; - FS.checkOpExists(setattr, 63); - setattr(arg, attr); - }, - chrdev_stream_ops: { - open(stream) { - stream.stream_ops = FS.getDevice(stream.node.rdev).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); - } - for (var mount of mounts) if (mount.type.syncfs) mount.type.syncfs(mount, populate, done); - else done(null); - }, - 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); - for (var [hash, current] of Object.entries(FS.nameTable)) 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 parent = FS.lookupPath(path, { parent: true }).node; - var name = PATH.basename(path); - if (!name) throw new FS.ErrnoError(28); - if (name === "." || name === "..") throw new FS.ErrnoError(20); - 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); - }, - statfs(path) { - return FS.statfsNode(FS.lookupPath(path, { follow: true }).node); - }, - statfsStream(stream) { - return FS.statfsNode(stream.node); - }, - statfsNode(node) { - var rtn = { - bsize: 4096, - frsize: 4096, - blocks: 1e6, - bfree: 5e5, - bavail: 5e5, - files: FS.nextInode, - ffree: FS.nextInode - 1, - fsid: 42, - flags: 2, - namelen: 255 - }; - if (node.node_ops.statfs) Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root)); - return rtn; - }, - create(path, mode = 438) { - mode &= 4095; - mode |= 32768; - return FS.mknod(path, mode, 0); - }, - mkdir(path, mode = 511) { - mode &= 1023; - mode |= 16384; - return FS.mknod(path, mode, 0); - }, - mkdirTree(path, mode) { - var dirs = path.split("/"); - var d = ""; - for (var dir of dirs) { - if (!dir) continue; - if (d || PATH.isAbs(path)) d += "/"; - d += dir; - try { - FS.mkdir(d, mode); - } catch (e) { - if (e.errno != 20) throw e; - } - } - }, - mkdev(path, mode, dev) { - if (typeof dev == "undefined") { - dev = mode; - mode = 438; - } - mode |= 8192; - return FS.mknod(path, mode, dev); - }, - symlink(oldpath, newpath) { - if (!PATH_FS.resolve(oldpath)) throw new FS.ErrnoError(44); - var parent = FS.lookupPath(newpath, { parent: true }).node; - if (!parent) throw new FS.ErrnoError(44); - var newname = PATH.basename(newpath); - var errCode = FS.mayCreate(parent, newname); - if (errCode) throw new FS.ErrnoError(errCode); - if (!parent.node_ops.symlink) throw new FS.ErrnoError(63); - return parent.node_ops.symlink(parent, newname, oldpath); - }, - rename(old_path, new_path) { - var old_dirname = PATH.dirname(old_path); - var new_dirname = PATH.dirname(new_path); - var old_name = PATH.basename(old_path); - var new_name = PATH.basename(new_path); - var lookup = FS.lookupPath(old_path, { parent: true }), old_dir = lookup.node, new_dir; - lookup = FS.lookupPath(new_path, { parent: true }); - new_dir = lookup.node; - if (!old_dir || !new_dir) throw new FS.ErrnoError(44); - if (old_dir.mount !== new_dir.mount) throw new FS.ErrnoError(75); - var old_node = FS.lookupNode(old_dir, old_name); - var relative = PATH_FS.relative(old_path, new_dirname); - if (relative.charAt(0) !== ".") throw new FS.ErrnoError(28); - relative = PATH_FS.relative(new_path, old_dirname); - if (relative.charAt(0) !== ".") throw new FS.ErrnoError(55); - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) {} - if (old_node === new_node) return; - var isdir = FS.isDir(old_node.mode); - var errCode = FS.mayDelete(old_dir, old_name, isdir); - if (errCode) throw new FS.ErrnoError(errCode); - errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); - if (errCode) throw new FS.ErrnoError(errCode); - if (!old_dir.node_ops.rename) throw new FS.ErrnoError(63); - if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) throw new FS.ErrnoError(10); - if (new_dir !== old_dir) { - errCode = FS.nodePermissions(old_dir, "w"); - if (errCode) throw new FS.ErrnoError(errCode); - } - FS.hashRemoveNode(old_node); - try { - old_dir.node_ops.rename(old_node, new_dir, new_name); - old_node.parent = new_dir; - } catch (e) { - throw e; - } finally { - FS.hashAddNode(old_node); - } - }, - rmdir(path) { - var parent = FS.lookupPath(path, { parent: true }).node; - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var errCode = FS.mayDelete(parent, name, true); - if (errCode) throw new FS.ErrnoError(errCode); - if (!parent.node_ops.rmdir) throw new FS.ErrnoError(63); - if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); - parent.node_ops.rmdir(parent, name); - FS.destroyNode(node); - }, - readdir(path) { - var node = FS.lookupPath(path, { follow: true }).node; - return FS.checkOpExists(node.node_ops.readdir, 54)(node); - }, - unlink(path) { - var parent = FS.lookupPath(path, { parent: true }).node; - if (!parent) throw new FS.ErrnoError(44); - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var errCode = FS.mayDelete(parent, name, false); - if (errCode) throw new FS.ErrnoError(errCode); - if (!parent.node_ops.unlink) throw new FS.ErrnoError(63); - if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); - parent.node_ops.unlink(parent, name); - FS.destroyNode(node); - }, - readlink(path) { - var link = FS.lookupPath(path).node; - if (!link) throw new FS.ErrnoError(44); - if (!link.node_ops.readlink) throw new FS.ErrnoError(28); - return link.node_ops.readlink(link); - }, - stat(path, dontFollow) { - var node = FS.lookupPath(path, { follow: !dontFollow }).node; - return FS.checkOpExists(node.node_ops.getattr, 63)(node); - }, - fstat(fd) { - var stream = FS.getStreamChecked(fd); - var node = stream.node; - var getattr = stream.stream_ops.getattr; - var arg = getattr ? stream : node; - getattr ??= node.node_ops.getattr; - FS.checkOpExists(getattr, 63); - return getattr(arg); - }, - lstat(path) { - return FS.stat(path, true); - }, - doChmod(stream, node, mode, dontFollow) { - FS.doSetAttr(stream, node, { - mode: mode & 4095 | node.mode & -4096, - ctime: Date.now(), - dontFollow - }); - }, - chmod(path, mode, dontFollow) { - var node; - if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; - else node = path; - FS.doChmod(null, node, mode, dontFollow); - }, - lchmod(path, mode) { - FS.chmod(path, mode, true); - }, - fchmod(fd, mode) { - var stream = FS.getStreamChecked(fd); - FS.doChmod(stream, stream.node, mode, false); - }, - doChown(stream, node, dontFollow) { - FS.doSetAttr(stream, node, { - timestamp: Date.now(), - dontFollow - }); - }, - chown(path, uid, gid, dontFollow) { - var node; - if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; - else node = path; - FS.doChown(null, node, dontFollow); - }, - lchown(path, uid, gid) { - FS.chown(path, uid, gid, true); - }, - fchown(fd, uid, gid) { - var stream = FS.getStreamChecked(fd); - FS.doChown(stream, stream.node, false); - }, - doTruncate(stream, node, len) { - if (FS.isDir(node.mode)) throw new FS.ErrnoError(31); - if (!FS.isFile(node.mode)) throw new FS.ErrnoError(28); - var errCode = FS.nodePermissions(node, "w"); - if (errCode) throw new FS.ErrnoError(errCode); - FS.doSetAttr(stream, node, { - size: len, - timestamp: Date.now() - }); - }, - truncate(path, len) { - if (len < 0) throw new FS.ErrnoError(28); - var node; - if (typeof path == "string") node = FS.lookupPath(path, { follow: true }).node; - else node = path; - FS.doTruncate(null, node, len); - }, - ftruncate(fd, len) { - var stream = FS.getStreamChecked(fd); - if (len < 0 || (stream.flags & 2097155) === 0) throw new FS.ErrnoError(28); - FS.doTruncate(stream, stream.node, len); - }, - utime(path, atime, mtime) { - var node = FS.lookupPath(path, { follow: true }).node; - FS.checkOpExists(node.node_ops.setattr, 63)(node, { - atime, - mtime - }); - }, - open(path, flags, mode = 438) { - if (path === "") throw new FS.ErrnoError(44); - flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; - if (flags & 64) mode = mode & 4095 | 32768; - else mode = 0; - var node; - var isDirPath; - if (typeof path == "object") node = path; - else { - isDirPath = path.endsWith("/"); - var lookup = FS.lookupPath(path, { - follow: !(flags & 131072), - noent_okay: true - }); - node = lookup.node; - path = lookup.path; - } - var created = false; - if (flags & 64) if (node) { - if (flags & 128) throw new FS.ErrnoError(20); - } else if (isDirPath) throw new FS.ErrnoError(31); - else { - node = FS.mknod(path, mode | 511, 0); - created = true; - } - if (!node) throw new FS.ErrnoError(44); - if (FS.isChrdev(node.mode)) flags &= -513; - if (flags & 65536 && !FS.isDir(node.mode)) throw new FS.ErrnoError(54); - if (!created) { - var errCode = FS.mayOpen(node, flags); - if (errCode) throw new FS.ErrnoError(errCode); - } - if (flags & 512 && !created) FS.truncate(node, 0); - flags &= -131713; - var stream = FS.createStream({ - node, - path: FS.getPath(node), - flags, - seekable: true, - position: 0, - stream_ops: node.stream_ops, - ungotten: [], - error: false - }); - if (stream.stream_ops.open) stream.stream_ops.open(stream); - if (created) FS.chmod(node, mode & 511); - if (Module["logReadFiles"] && !(flags & 1)) { - if (!(path in FS.readFiles)) FS.readFiles[path] = 1; - } - return stream; - }, - close(stream) { - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if (stream.getdents) stream.getdents = null; - try { - if (stream.stream_ops.close) stream.stream_ops.close(stream); - } catch (e) { - throw e; - } finally { - FS.closeStream(stream.fd); - } - stream.fd = null; - }, - isClosed(stream) { - return stream.fd === null; - }, - llseek(stream, offset, whence) { - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if (!stream.seekable || !stream.stream_ops.llseek) throw new FS.ErrnoError(70); - if (whence != 0 && whence != 1 && whence != 2) throw new FS.ErrnoError(28); - stream.position = stream.stream_ops.llseek(stream, offset, whence); - stream.ungotten = []; - return stream.position; - }, - read(stream, buffer, offset, length, position) { - if (length < 0 || position < 0) throw new FS.ErrnoError(28); - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(8); - if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); - if (!stream.stream_ops.read) throw new FS.ErrnoError(28); - var seeking = typeof position != "undefined"; - if (!seeking) position = stream.position; - else if (!stream.seekable) throw new FS.ErrnoError(70); - var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); - if (!seeking) stream.position += bytesRead; - return bytesRead; - }, - write(stream, buffer, offset, length, position, canOwn) { - if (length < 0 || position < 0) throw new FS.ErrnoError(28); - if (FS.isClosed(stream)) throw new FS.ErrnoError(8); - if ((stream.flags & 2097155) === 0) throw new FS.ErrnoError(8); - if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31); - if (!stream.stream_ops.write) throw new FS.ErrnoError(28); - if (stream.seekable && stream.flags & 1024) FS.llseek(stream, 0, 2); - var seeking = typeof position != "undefined"; - if (!seeking) position = stream.position; - else if (!stream.seekable) throw new FS.ErrnoError(70); - var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); - if (!seeking) stream.position += bytesWritten; - return bytesWritten; - }, - mmap(stream, length, position, prot, flags) { - if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) throw new FS.ErrnoError(2); - if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(2); - if (!stream.stream_ops.mmap) throw new FS.ErrnoError(43); - if (!length) throw new FS.ErrnoError(28); - return stream.stream_ops.mmap(stream, length, position, prot, flags); - }, - msync(stream, buffer, offset, length, mmapFlags) { - if (!stream.stream_ops.msync) return 0; - return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); - }, - ioctl(stream, cmd, arg) { - if (!stream.stream_ops.ioctl) throw new FS.ErrnoError(59); - return stream.stream_ops.ioctl(stream, cmd, arg); - }, - readFile(path, opts = {}) { - opts.flags = opts.flags || 0; - opts.encoding = opts.encoding || "binary"; - if (opts.encoding !== "utf8" && opts.encoding !== "binary") abort(`Invalid encoding type "${opts.encoding}"`); - var stream = FS.open(path, opts.flags); - var length = FS.stat(path).size; - var buf = new Uint8Array(length); - FS.read(stream, buf, 0, length, 0); - if (opts.encoding === "utf8") buf = UTF8ArrayToString(buf); - FS.close(stream); - return buf; - }, - writeFile(path, data, opts = {}) { - opts.flags = opts.flags || 577; - var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data == "string") data = new Uint8Array(intArrayFromString(data, true)); - if (ArrayBuffer.isView(data)) FS.write(stream, data, 0, data.byteLength, void 0, opts.canOwn); - else abort("Unsupported data type"); - FS.close(stream); - }, - cwd: () => FS.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, - llseek: () => 0 - }); - 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) { - randomFill(randomBuffer); - randomLeft = 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", 16895, 73); - node.stream_ops = { llseek: MEMFS.stream_ops.llseek }; - 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 }, - id: fd + 1 - }; - ret.parent = ret; - return ret; - }, - readdir() { - return Array.from(FS.streams.entries()).filter(([k, v]) => v).map(([k, v]) => k.toString()); - } - }; - 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"); - FS.open("/dev/stdin", 0); - FS.open("/dev/stdout", 1); - FS.open("/dev/stderr", 1); - }, - staticInit() { - FS.nameTable = new Array(4096); - FS.mount(MEMFS, {}, "/"); - FS.createDefaultDirectories(); - FS.createDefaultDevices(); - FS.createSpecialDirectories(); - FS.filesystems = { "MEMFS": 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 stream of FS.streams) if (stream) FS.close(stream); - }, - findObject(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (!ret.exists) return null; - return ret.object; - }, - analyzePath(path, dontResolveLastLink) { - try { - var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - path = lookup.path; - } catch (e) {} - var ret = { - isRoot: false, - exists: false, - error: 0, - name: null, - path: null, - object: null, - parentExists: false, - parentPath: null, - parentObject: null - }; - try { - var lookup = FS.lookupPath(path, { parent: true }); - ret.parentExists = true; - ret.parentPath = lookup.path; - ret.parentObject = lookup.node; - ret.name = PATH.basename(path); - lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - ret.exists = true; - ret.path = lookup.path; - ret.object = lookup.node; - ret.name = lookup.node.name; - ret.isRoot = lookup.path === "/"; - } catch (e) { - ret.error = e.errno; - } - return ret; - }, - createPath(parent, path, canRead, canWrite) { - parent = typeof parent == "string" ? parent : FS.getPath(parent); - var parts = path.split("/").reverse(); - while (parts.length) { - var part = parts.pop(); - if (!part) continue; - var current = PATH.join2(parent, part); - try { - FS.mkdir(current); - } catch (e) { - if (e.errno != 20) throw e; - } - parent = current; - } - return current; - }, - createFile(parent, name, properties, canRead, canWrite) { - var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); - var mode = FS_getMode(canRead, canWrite); - return FS.create(path, mode); - }, - createDataFile(parent, name, data, canRead, canWrite, canOwn) { - var path = name; - if (parent) { - parent = typeof parent == "string" ? parent : FS.getPath(parent); - path = name ? PATH.join2(parent, name) : parent; - } - var mode = FS_getMode(canRead, canWrite); - var node = FS.create(path, mode); - if (data) { - if (typeof data == "string") { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } - FS.chmod(node, mode | 146); - var stream = FS.open(node, 577); - FS.write(stream, data, 0, data.length, 0, canOwn); - FS.close(stream); - FS.chmod(node, mode); - } - }, - createDevice(parent, name, input, output) { - var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name); - var mode = FS_getMode(!!input, !!output); - FS.createDevice.major ??= 64; - var dev = FS.makedev(FS.createDevice.major++, 0); - FS.registerDevice(dev, { - open(stream) { - stream.seekable = false; - }, - close(stream) { - if (output?.buffer?.length) output(10); - }, - read(stream, buffer, offset, length, pos) { - var bytesRead = 0; - for (var i = 0; i < length; i++) { - var result; - try { - result = input(); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); - if (result === null || result === void 0) break; - bytesRead++; - buffer[offset + i] = result; - } - if (bytesRead) stream.node.atime = Date.now(); - return bytesRead; - }, - write(stream, buffer, offset, length, pos) { - for (var i = 0; i < length; i++) try { - output(buffer[offset + i]); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (length) stream.node.mtime = stream.node.ctime = Date.now(); - return i; - } - }); - return FS.mkdev(path, mode, dev); - }, - forceLoadFile(obj) { - if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; - if (globalThis.XMLHttpRequest) abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - else try { - obj.contents = readBinary(obj.url); - } catch (e) { - throw new FS.ErrnoError(29); - } - }, - createLazyFile(parent, name, url, canRead, canWrite) { - class LazyUint8Array { - lengthKnown = false; - chunks = []; - get(idx) { - if (idx > this.length - 1 || idx < 0) return; - 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)) abort("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) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength - 1) abort("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)) abort("Couldn't load " + url + ". Status: " + xhr.status); - if (xhr.response !== void 0) 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") abort("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 (globalThis.XMLHttpRequest) { - if (!ENVIRONMENT_IS_WORKER) abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"); - var properties = { - isDevice: false, - contents: new LazyUint8Array() - }; - } 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 = {}; - for (const [key, fn] of Object.entries(node.stream_ops)) 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 < size; i++) buffer[offset + i] = contents[position + i]; - else for (var i = 0; i < size; i++) buffer[offset + i] = contents.get(position + i); - return size; - } - stream_ops.read = (stream, buffer, offset, length, position) => { - 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; - } - }; - /** - * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the - * emscripten HEAP, returns a copy of that string as a Javascript String object. - * - * @param {number} ptr - * @param {number=} maxBytesToRead - An optional length that specifies the - * maximum number of bytes to read. You can omit this parameter to scan the - * string until the first 0 byte. If maxBytesToRead is passed, and the string - * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the - * string will cut short at that byte index. - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ - var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { - if (!ptr) return ""; - var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); - return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); - }; - var SYSCALLS = { - calculateAt(dirfd, path, allowEmpty) { - if (PATH.isAbs(path)) return path; - var dir; - if (dirfd === -100) dir = FS.cwd(); - else dir = SYSCALLS.getStreamFromFD(dirfd).path; - if (path.length == 0) { - if (!allowEmpty) throw new FS.ErrnoError(44); - return dir; - } - return dir + "/" + path; - }, - writeStat(buf, stat) { - HEAPU32[buf >> 2] = stat.dev; - HEAPU32[buf + 4 >> 2] = stat.mode; - HEAPU32[buf + 8 >> 2] = stat.nlink; - HEAPU32[buf + 12 >> 2] = stat.uid; - HEAPU32[buf + 16 >> 2] = stat.gid; - HEAPU32[buf + 20 >> 2] = stat.rdev; - HEAP64[buf + 24 >> 3] = BigInt(stat.size); - 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(); - HEAP64[buf + 40 >> 3] = BigInt(Math.floor(atime / 1e3)); - HEAPU32[buf + 48 >> 2] = atime % 1e3 * 1e3 * 1e3; - HEAP64[buf + 56 >> 3] = BigInt(Math.floor(mtime / 1e3)); - HEAPU32[buf + 64 >> 2] = mtime % 1e3 * 1e3 * 1e3; - HEAP64[buf + 72 >> 3] = BigInt(Math.floor(ctime / 1e3)); - HEAPU32[buf + 80 >> 2] = ctime % 1e3 * 1e3 * 1e3; - HEAP64[buf + 88 >> 3] = BigInt(stat.ino); - return 0; - }, - writeStatFs(buf, stats) { - HEAPU32[buf + 4 >> 2] = stats.bsize; - HEAPU32[buf + 60 >> 2] = stats.bsize; - HEAP64[buf + 8 >> 3] = BigInt(stats.blocks); - HEAP64[buf + 16 >> 3] = BigInt(stats.bfree); - HEAP64[buf + 24 >> 3] = BigInt(stats.bavail); - HEAP64[buf + 32 >> 3] = BigInt(stats.files); - HEAP64[buf + 40 >> 3] = BigInt(stats.ffree); - HEAPU32[buf + 48 >> 2] = stats.fsid; - HEAPU32[buf + 64 >> 2] = stats.flags; - HEAPU32[buf + 56 >> 2] = stats.namelen; - }, - 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) { - return FS.getStreamChecked(fd); - }, - varargs: void 0, - getStr(ptr) { - return UTF8ToString(ptr); - } - }; - 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 & -8) return -28; - var node = FS.lookupPath(path, { follow: true }).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; - } - } - var 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++; - return FS.dupStream(stream, arg).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 { - return SYSCALLS.writeStat(buf, FS.fstat(fd)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - var INT53_MAX = 9007199254740992; - var INT53_MIN = -9007199254740992; - var bigintToI53Checked = (num) => num < INT53_MIN || num > INT53_MAX ? NaN : Number(num); - function ___syscall_ftruncate64(fd, length) { - length = bigintToI53Checked(length); - 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) => { - return 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 (size < cwdLengthInBytes) return -68; - stringToUTF8(cwd, buf, size); - return cwdLengthInBytes; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_ioctl(fd, op, varargs) { - SYSCALLS.varargs = varargs; - try { - var stream = SYSCALLS.getStreamFromFD(fd); - switch (op) { - case 21509: - if (!stream.tty) return -59; - return 0; - case 21505: - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tcgets) { - var termios = stream.tty.ops.ioctl_tcgets(stream); - var argp = syscallGetVarargP(); - HEAP32[argp >> 2] = termios.c_iflag || 0; - HEAP32[argp + 4 >> 2] = termios.c_oflag || 0; - HEAP32[argp + 8 >> 2] = termios.c_cflag || 0; - HEAP32[argp + 12 >> 2] = termios.c_lflag || 0; - for (var i = 0; i < 32; i++) HEAP8[argp + i + 17] = termios.c_cc[i] || 0; - return 0; - } - return 0; - case 21510: - case 21511: - case 21512: - if (!stream.tty) return -59; - return 0; - case 21506: - case 21507: - case 21508: - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tcsets) { - var argp = syscallGetVarargP(); - var c_iflag = HEAP32[argp >> 2]; - var c_oflag = HEAP32[argp + 4 >> 2]; - var c_cflag = HEAP32[argp + 8 >> 2]; - var c_lflag = HEAP32[argp + 12 >> 2]; - var c_cc = []; - for (var i = 0; i < 32; i++) c_cc.push(HEAP8[argp + i + 17]); - return stream.tty.ops.ioctl_tcsets(stream.tty, op, { - c_iflag, - c_oflag, - c_cflag, - c_lflag, - c_cc - }); - } - return 0; - case 21519: - if (!stream.tty) return -59; - var argp = syscallGetVarargP(); - HEAP32[argp >> 2] = 0; - return 0; - case 21520: - if (!stream.tty) return -59; - return -28; - case 21537: - case 21531: - var argp = syscallGetVarargP(); - return FS.ioctl(stream, op, argp); - case 21523: - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tiocgwinsz) { - var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); - var argp = syscallGetVarargP(); - HEAP16[argp >> 1] = winsize[0]; - HEAP16[argp + 2 >> 1] = winsize[1]; - } - return 0; - case 21524: - if (!stream.tty) return -59; - return 0; - case 21515: - if (!stream.tty) return -59; - return 0; - default: return -28; - } - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_lstat64(path, buf) { - try { - path = SYSCALLS.getStr(path); - return SYSCALLS.writeStat(buf, FS.lstat(path)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_mkdirat(dirfd, path, mode) { - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - FS.mkdir(path, mode, 0); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_newfstatat(dirfd, path, buf, flags) { - try { - path = SYSCALLS.getStr(path); - var nofollow = flags & 256; - var allowEmpty = flags & 4096; - flags = flags & -6401; - path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); - return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_openat(dirfd, path, flags, varargs) { - SYSCALLS.varargs = varargs; - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - var mode = varargs ? syscallGetVarargI() : 0; - return FS.open(path, flags, mode).fd; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_readlinkat(dirfd, path, buf, bufsize) { - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (bufsize <= 0) return -28; - var ret = FS.readlink(path); - var len = Math.min(bufsize, lengthBytesUTF8(ret)); - var endChar = HEAP8[buf + len]; - stringToUTF8(ret, buf, bufsize + 1); - HEAP8[buf + len] = endChar; - return len; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_rmdir(path) { - try { - path = SYSCALLS.getStr(path); - FS.rmdir(path); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_stat64(path, buf) { - try { - path = SYSCALLS.getStr(path); - return SYSCALLS.writeStat(buf, FS.stat(path)); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - function ___syscall_unlinkat(dirfd, path, flags) { - try { - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (!flags) FS.unlink(path); - else if (flags === 512) FS.rmdir(path); - else return -28; - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - var readI53FromI64 = (ptr) => { - return HEAPU32[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 = null; - 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 = null; - else mtime = seconds * 1e3 + nanoseconds / (1e3 * 1e3); - } - if ((mtime ?? atime) !== null) FS.utime(path, atime, mtime); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return -e.errno; - } - } - 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) => { - return (isLeapYear(date.getFullYear()) ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE)[date.getMonth()] + date.getDate() - 1; - }; - function __localtime_js(time, tmPtr) { - time = bigintToI53Checked(time); - var date = /* @__PURE__ */ 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, allocated, addr) { - offset = bigintToI53Checked(offset); - try { - 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) { - offset = bigintToI53Checked(offset); - 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 __tzset_js = (timezone, daylight, std_name, dst_name) => { - var currentYear = (/* @__PURE__ */ 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); - return `UTC${sign}${String(Math.floor(absOffset / 60)).padStart(2, "0")}${String(absOffset % 60).padStart(2, "0")}`; - }; - var winterName = extractZone(winterOffset); - var summerName = extractZone(summerOffset); - if (summerOffset < winterOffset) { - stringToUTF8(winterName, std_name, 17); - stringToUTF8(summerName, dst_name, 17); - } else { - stringToUTF8(winterName, dst_name, 17); - stringToUTF8(summerName, std_name, 17); - } - }; - var _emscripten_get_now = () => performance.now(); - var _emscripten_date_now = () => Date.now(); - var nowIsMonotonic = 1; - var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; - function _clock_time_get(clk_id, ignored_precision, ptime) { - ignored_precision = bigintToI53Checked(ignored_precision); - if (!checkWasiClock(clk_id)) return 28; - var now; - if (clk_id === 0) now = _emscripten_date_now(); - else if (nowIsMonotonic) now = _emscripten_get_now(); - else return 52; - var nsec = Math.round(now * 1e3 * 1e3); - HEAP64[ptime >> 3] = BigInt(nsec); - return 0; - } - var getHeapMax = () => 2147483648; - var _emscripten_get_heap_max = () => getHeapMax(); - var growMemory = (size) => { - var pages = (size - wasmMemory.buffer.byteLength + 65535) / 65536 | 0; - 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); - if (growMemory(Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)))) return true; - } - return false; - }; - var ENV = {}; - var getExecutableName = () => thisProgram || "./this.program"; - var getEnvStrings = () => { - if (!getEnvStrings.strings) { - var lang = (globalThis.navigator?.language ?? "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] === void 0) 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 _environ_get = (__environ, environ_buf) => { - var bufSize = 0; - var envp = 0; - for (var string of getEnvStrings()) { - var ptr = environ_buf + bufSize; - HEAPU32[__environ + envp >> 2] = ptr; - bufSize += stringToUTF8(string, ptr, Infinity) + 1; - envp += 4; - } - return 0; - }; - var _environ_sizes_get = (penviron_count, penviron_buf_size) => { - var strings = getEnvStrings(); - HEAPU32[penviron_count >> 2] = strings.length; - var bufSize = 0; - for (var string of strings) bufSize += lengthBytesUTF8(string) + 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; - HEAP64[pbuf + 8 >> 3] = BigInt(rightsBase); - HEAP64[pbuf + 16 >> 3] = BigInt(rightsInheriting); - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - /** @param {number=} offset */ - var doReadv = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAPU32[iov >> 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 < len) break; - if (typeof offset != "undefined") offset += curr; - } - return ret; - }; - function _fd_read(fd, iov, iovcnt, pnum) { - try { - var num = doReadv(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); - HEAPU32[pnum >> 2] = num; - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - function _fd_seek(fd, offset, whence, newOffset) { - offset = bigintToI53Checked(offset); - try { - if (isNaN(offset)) return 61; - var stream = SYSCALLS.getStreamFromFD(fd); - FS.llseek(stream, offset, whence); - HEAP64[newOffset >> 3] = BigInt(stream.position); - 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); - return stream.stream_ops?.fsync?.(stream); - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - /** @param {number=} offset */ - var doWritev = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAPU32[iov >> 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 < len) break; - if (typeof offset != "undefined") offset += curr; - } - return ret; - }; - function _fd_write(fd, iov, iovcnt, pnum) { - try { - var num = doWritev(SYSCALLS.getStreamFromFD(fd), iov, iovcnt); - HEAPU32[pnum >> 2] = num; - return 0; - } catch (e) { - if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; - return e.errno; - } - } - FS.createPreloadedFile = FS_createPreloadedFile; - FS.preloadFile = FS_preloadFile; - FS.staticInit(); - initMemory(); - if (Module["noExitRuntime"]) Module["noExitRuntime"]; - if (Module["preloadPlugins"]) preloadPlugins = Module["preloadPlugins"]; - if (Module["print"]) out = Module["print"]; - if (Module["printErr"]) err = Module["printErr"]; - if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; - if (Module["arguments"]) Module["arguments"]; - if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; - if (Module["preInit"]) { - if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; - while (Module["preInit"].length > 0) Module["preInit"].shift()(); - } - Module["wasmMemory"] = wasmMemory; - var _emscripten_builtin_memalign; - function assignWasmExports(wasmExports) { - Module["_sqlite3_status64"] = wasmExports["sqlite3_status64"]; - Module["_sqlite3_status"] = wasmExports["sqlite3_status"]; - Module["_sqlite3_db_status64"] = wasmExports["sqlite3_db_status64"]; - Module["_sqlite3_msize"] = wasmExports["sqlite3_msize"]; - Module["_sqlite3_db_status"] = wasmExports["sqlite3_db_status"]; - Module["_sqlite3_vfs_find"] = wasmExports["sqlite3_vfs_find"]; - Module["_sqlite3_initialize"] = wasmExports["sqlite3_initialize"]; - Module["_sqlite3_malloc"] = wasmExports["sqlite3_malloc"]; - Module["_sqlite3_free"] = wasmExports["sqlite3_free"]; - Module["_sqlite3_vfs_register"] = wasmExports["sqlite3_vfs_register"]; - Module["_sqlite3_vfs_unregister"] = wasmExports["sqlite3_vfs_unregister"]; - Module["_sqlite3_malloc64"] = wasmExports["sqlite3_malloc64"]; - Module["_sqlite3_realloc"] = wasmExports["sqlite3_realloc"]; - Module["_sqlite3_realloc64"] = wasmExports["sqlite3_realloc64"]; - Module["_sqlite3_value_text"] = wasmExports["sqlite3_value_text"]; - Module["_sqlite3_randomness"] = wasmExports["sqlite3_randomness"]; - Module["_sqlite3_stricmp"] = wasmExports["sqlite3_stricmp"]; - Module["_sqlite3_strnicmp"] = wasmExports["sqlite3_strnicmp"]; - Module["_sqlite3_uri_parameter"] = wasmExports["sqlite3_uri_parameter"]; - Module["_sqlite3_uri_boolean"] = wasmExports["sqlite3_uri_boolean"]; - Module["_sqlite3_serialize"] = wasmExports["sqlite3_serialize"]; - Module["_sqlite3_prepare_v2"] = wasmExports["sqlite3_prepare_v2"]; - Module["_sqlite3_step"] = wasmExports["sqlite3_step"]; - Module["_sqlite3_column_int64"] = wasmExports["sqlite3_column_int64"]; - Module["_sqlite3_reset"] = wasmExports["sqlite3_reset"]; - Module["_sqlite3_exec"] = wasmExports["sqlite3_exec"]; - Module["_sqlite3_column_int"] = wasmExports["sqlite3_column_int"]; - Module["_sqlite3_finalize"] = wasmExports["sqlite3_finalize"]; - Module["_sqlite3_file_control"] = wasmExports["sqlite3_file_control"]; - Module["_sqlite3_column_name"] = wasmExports["sqlite3_column_name"]; - Module["_sqlite3_column_text"] = wasmExports["sqlite3_column_text"]; - Module["_sqlite3_column_type"] = wasmExports["sqlite3_column_type"]; - Module["_sqlite3_errmsg"] = wasmExports["sqlite3_errmsg"]; - Module["_sqlite3_deserialize"] = wasmExports["sqlite3_deserialize"]; - Module["_sqlite3_clear_bindings"] = wasmExports["sqlite3_clear_bindings"]; - Module["_sqlite3_value_blob"] = wasmExports["sqlite3_value_blob"]; - Module["_sqlite3_value_bytes"] = wasmExports["sqlite3_value_bytes"]; - Module["_sqlite3_value_double"] = wasmExports["sqlite3_value_double"]; - Module["_sqlite3_value_int"] = wasmExports["sqlite3_value_int"]; - Module["_sqlite3_value_int64"] = wasmExports["sqlite3_value_int64"]; - Module["_sqlite3_value_subtype"] = wasmExports["sqlite3_value_subtype"]; - Module["_sqlite3_value_pointer"] = wasmExports["sqlite3_value_pointer"]; - Module["_sqlite3_value_type"] = wasmExports["sqlite3_value_type"]; - Module["_sqlite3_value_nochange"] = wasmExports["sqlite3_value_nochange"]; - Module["_sqlite3_value_frombind"] = wasmExports["sqlite3_value_frombind"]; - Module["_sqlite3_value_dup"] = wasmExports["sqlite3_value_dup"]; - Module["_sqlite3_value_free"] = wasmExports["sqlite3_value_free"]; - Module["_sqlite3_result_blob"] = wasmExports["sqlite3_result_blob"]; - Module["_sqlite3_result_error_toobig"] = wasmExports["sqlite3_result_error_toobig"]; - Module["_sqlite3_result_error_nomem"] = wasmExports["sqlite3_result_error_nomem"]; - Module["_sqlite3_result_double"] = wasmExports["sqlite3_result_double"]; - Module["_sqlite3_result_error"] = wasmExports["sqlite3_result_error"]; - Module["_sqlite3_result_int"] = wasmExports["sqlite3_result_int"]; - Module["_sqlite3_result_int64"] = wasmExports["sqlite3_result_int64"]; - Module["_sqlite3_result_null"] = wasmExports["sqlite3_result_null"]; - Module["_sqlite3_result_pointer"] = wasmExports["sqlite3_result_pointer"]; - Module["_sqlite3_result_subtype"] = wasmExports["sqlite3_result_subtype"]; - Module["_sqlite3_result_text"] = wasmExports["sqlite3_result_text"]; - Module["_sqlite3_result_zeroblob"] = wasmExports["sqlite3_result_zeroblob"]; - Module["_sqlite3_result_zeroblob64"] = wasmExports["sqlite3_result_zeroblob64"]; - Module["_sqlite3_result_error_code"] = wasmExports["sqlite3_result_error_code"]; - Module["_sqlite3_user_data"] = wasmExports["sqlite3_user_data"]; - Module["_sqlite3_context_db_handle"] = wasmExports["sqlite3_context_db_handle"]; - Module["_sqlite3_vtab_nochange"] = wasmExports["sqlite3_vtab_nochange"]; - Module["_sqlite3_vtab_in_first"] = wasmExports["sqlite3_vtab_in_first"]; - Module["_sqlite3_vtab_in_next"] = wasmExports["sqlite3_vtab_in_next"]; - Module["_sqlite3_aggregate_context"] = wasmExports["sqlite3_aggregate_context"]; - Module["_sqlite3_get_auxdata"] = wasmExports["sqlite3_get_auxdata"]; - Module["_sqlite3_set_auxdata"] = wasmExports["sqlite3_set_auxdata"]; - Module["_sqlite3_column_count"] = wasmExports["sqlite3_column_count"]; - Module["_sqlite3_data_count"] = wasmExports["sqlite3_data_count"]; - Module["_sqlite3_column_blob"] = wasmExports["sqlite3_column_blob"]; - Module["_sqlite3_column_bytes"] = wasmExports["sqlite3_column_bytes"]; - Module["_sqlite3_column_double"] = wasmExports["sqlite3_column_double"]; - Module["_sqlite3_column_value"] = wasmExports["sqlite3_column_value"]; - Module["_sqlite3_column_decltype"] = wasmExports["sqlite3_column_decltype"]; - Module["_sqlite3_column_database_name"] = wasmExports["sqlite3_column_database_name"]; - Module["_sqlite3_column_table_name"] = wasmExports["sqlite3_column_table_name"]; - Module["_sqlite3_column_origin_name"] = wasmExports["sqlite3_column_origin_name"]; - Module["_sqlite3_bind_blob"] = wasmExports["sqlite3_bind_blob"]; - Module["_sqlite3_bind_double"] = wasmExports["sqlite3_bind_double"]; - Module["_sqlite3_bind_int"] = wasmExports["sqlite3_bind_int"]; - Module["_sqlite3_bind_int64"] = wasmExports["sqlite3_bind_int64"]; - Module["_sqlite3_bind_null"] = wasmExports["sqlite3_bind_null"]; - Module["_sqlite3_bind_pointer"] = wasmExports["sqlite3_bind_pointer"]; - Module["_sqlite3_bind_text"] = wasmExports["sqlite3_bind_text"]; - Module["_sqlite3_bind_parameter_count"] = wasmExports["sqlite3_bind_parameter_count"]; - Module["_sqlite3_bind_parameter_name"] = wasmExports["sqlite3_bind_parameter_name"]; - Module["_sqlite3_bind_parameter_index"] = wasmExports["sqlite3_bind_parameter_index"]; - Module["_sqlite3_db_handle"] = wasmExports["sqlite3_db_handle"]; - Module["_sqlite3_stmt_readonly"] = wasmExports["sqlite3_stmt_readonly"]; - Module["_sqlite3_stmt_isexplain"] = wasmExports["sqlite3_stmt_isexplain"]; - Module["_sqlite3_stmt_explain"] = wasmExports["sqlite3_stmt_explain"]; - Module["_sqlite3_stmt_busy"] = wasmExports["sqlite3_stmt_busy"]; - Module["_sqlite3_next_stmt"] = wasmExports["sqlite3_next_stmt"]; - Module["_sqlite3_stmt_status"] = wasmExports["sqlite3_stmt_status"]; - Module["_sqlite3_sql"] = wasmExports["sqlite3_sql"]; - Module["_sqlite3_expanded_sql"] = wasmExports["sqlite3_expanded_sql"]; - Module["_sqlite3_preupdate_old"] = wasmExports["sqlite3_preupdate_old"]; - Module["_sqlite3_preupdate_count"] = wasmExports["sqlite3_preupdate_count"]; - Module["_sqlite3_preupdate_depth"] = wasmExports["sqlite3_preupdate_depth"]; - Module["_sqlite3_preupdate_blobwrite"] = wasmExports["sqlite3_preupdate_blobwrite"]; - Module["_sqlite3_preupdate_new"] = wasmExports["sqlite3_preupdate_new"]; - Module["_sqlite3_value_numeric_type"] = wasmExports["sqlite3_value_numeric_type"]; - Module["_sqlite3_set_authorizer"] = wasmExports["sqlite3_set_authorizer"]; - Module["_sqlite3_strglob"] = wasmExports["sqlite3_strglob"]; - Module["_sqlite3_strlike"] = wasmExports["sqlite3_strlike"]; - Module["_sqlite3_auto_extension"] = wasmExports["sqlite3_auto_extension"]; - Module["_sqlite3_cancel_auto_extension"] = wasmExports["sqlite3_cancel_auto_extension"]; - Module["_sqlite3_reset_auto_extension"] = wasmExports["sqlite3_reset_auto_extension"]; - Module["_sqlite3_prepare_v3"] = wasmExports["sqlite3_prepare_v3"]; - Module["_sqlite3_create_module"] = wasmExports["sqlite3_create_module"]; - Module["_sqlite3_create_module_v2"] = wasmExports["sqlite3_create_module_v2"]; - Module["_sqlite3_drop_modules"] = wasmExports["sqlite3_drop_modules"]; - Module["_sqlite3_declare_vtab"] = wasmExports["sqlite3_declare_vtab"]; - Module["_sqlite3_vtab_on_conflict"] = wasmExports["sqlite3_vtab_on_conflict"]; - Module["_sqlite3_vtab_collation"] = wasmExports["sqlite3_vtab_collation"]; - Module["_sqlite3_vtab_in"] = wasmExports["sqlite3_vtab_in"]; - Module["_sqlite3_vtab_rhs_value"] = wasmExports["sqlite3_vtab_rhs_value"]; - Module["_sqlite3_vtab_distinct"] = wasmExports["sqlite3_vtab_distinct"]; - Module["_sqlite3_keyword_name"] = wasmExports["sqlite3_keyword_name"]; - Module["_sqlite3_keyword_count"] = wasmExports["sqlite3_keyword_count"]; - Module["_sqlite3_keyword_check"] = wasmExports["sqlite3_keyword_check"]; - Module["_sqlite3_complete"] = wasmExports["sqlite3_complete"]; - Module["_sqlite3_libversion"] = wasmExports["sqlite3_libversion"]; - Module["_sqlite3_libversion_number"] = wasmExports["sqlite3_libversion_number"]; - Module["_sqlite3_shutdown"] = wasmExports["sqlite3_shutdown"]; - Module["_sqlite3_last_insert_rowid"] = wasmExports["sqlite3_last_insert_rowid"]; - Module["_sqlite3_set_last_insert_rowid"] = wasmExports["sqlite3_set_last_insert_rowid"]; - Module["_sqlite3_changes64"] = wasmExports["sqlite3_changes64"]; - Module["_sqlite3_changes"] = wasmExports["sqlite3_changes"]; - Module["_sqlite3_total_changes64"] = wasmExports["sqlite3_total_changes64"]; - Module["_sqlite3_total_changes"] = wasmExports["sqlite3_total_changes"]; - Module["_sqlite3_txn_state"] = wasmExports["sqlite3_txn_state"]; - Module["_sqlite3_close_v2"] = wasmExports["sqlite3_close_v2"]; - Module["_sqlite3_busy_handler"] = wasmExports["sqlite3_busy_handler"]; - Module["_sqlite3_progress_handler"] = wasmExports["sqlite3_progress_handler"]; - Module["_sqlite3_busy_timeout"] = wasmExports["sqlite3_busy_timeout"]; - Module["_sqlite3_interrupt"] = wasmExports["sqlite3_interrupt"]; - Module["_sqlite3_is_interrupted"] = wasmExports["sqlite3_is_interrupted"]; - Module["_sqlite3_create_function"] = wasmExports["sqlite3_create_function"]; - Module["_sqlite3_create_function_v2"] = wasmExports["sqlite3_create_function_v2"]; - Module["_sqlite3_create_window_function"] = wasmExports["sqlite3_create_window_function"]; - Module["_sqlite3_overload_function"] = wasmExports["sqlite3_overload_function"]; - Module["_sqlite3_trace_v2"] = wasmExports["sqlite3_trace_v2"]; - Module["_sqlite3_commit_hook"] = wasmExports["sqlite3_commit_hook"]; - Module["_sqlite3_update_hook"] = wasmExports["sqlite3_update_hook"]; - Module["_sqlite3_rollback_hook"] = wasmExports["sqlite3_rollback_hook"]; - Module["_sqlite3_preupdate_hook"] = wasmExports["sqlite3_preupdate_hook"]; - Module["_sqlite3_set_errmsg"] = wasmExports["sqlite3_set_errmsg"]; - Module["_sqlite3_error_offset"] = wasmExports["sqlite3_error_offset"]; - Module["_sqlite3_errcode"] = wasmExports["sqlite3_errcode"]; - Module["_sqlite3_extended_errcode"] = wasmExports["sqlite3_extended_errcode"]; - Module["_sqlite3_errstr"] = wasmExports["sqlite3_errstr"]; - Module["_sqlite3_limit"] = wasmExports["sqlite3_limit"]; - Module["_sqlite3_open"] = wasmExports["sqlite3_open"]; - Module["_sqlite3_open_v2"] = wasmExports["sqlite3_open_v2"]; - Module["_sqlite3_create_collation"] = wasmExports["sqlite3_create_collation"]; - Module["_sqlite3_create_collation_v2"] = wasmExports["sqlite3_create_collation_v2"]; - Module["_sqlite3_collation_needed"] = wasmExports["sqlite3_collation_needed"]; - Module["_sqlite3_get_autocommit"] = wasmExports["sqlite3_get_autocommit"]; - Module["_sqlite3_table_column_metadata"] = wasmExports["sqlite3_table_column_metadata"]; - Module["_sqlite3_extended_result_codes"] = wasmExports["sqlite3_extended_result_codes"]; - Module["_sqlite3_uri_key"] = wasmExports["sqlite3_uri_key"]; - Module["_sqlite3_uri_int64"] = wasmExports["sqlite3_uri_int64"]; - Module["_sqlite3_db_name"] = wasmExports["sqlite3_db_name"]; - Module["_sqlite3_db_filename"] = wasmExports["sqlite3_db_filename"]; - Module["_sqlite3_db_readonly"] = wasmExports["sqlite3_db_readonly"]; - Module["_sqlite3_compileoption_used"] = wasmExports["sqlite3_compileoption_used"]; - Module["_sqlite3_compileoption_get"] = wasmExports["sqlite3_compileoption_get"]; - Module["_sqlite3session_diff"] = wasmExports["sqlite3session_diff"]; - Module["_sqlite3session_attach"] = wasmExports["sqlite3session_attach"]; - Module["_sqlite3session_create"] = wasmExports["sqlite3session_create"]; - Module["_sqlite3session_delete"] = wasmExports["sqlite3session_delete"]; - Module["_sqlite3session_table_filter"] = wasmExports["sqlite3session_table_filter"]; - Module["_sqlite3session_changeset"] = wasmExports["sqlite3session_changeset"]; - Module["_sqlite3session_changeset_strm"] = wasmExports["sqlite3session_changeset_strm"]; - Module["_sqlite3session_patchset_strm"] = wasmExports["sqlite3session_patchset_strm"]; - Module["_sqlite3session_patchset"] = wasmExports["sqlite3session_patchset"]; - Module["_sqlite3session_enable"] = wasmExports["sqlite3session_enable"]; - Module["_sqlite3session_indirect"] = wasmExports["sqlite3session_indirect"]; - Module["_sqlite3session_isempty"] = wasmExports["sqlite3session_isempty"]; - Module["_sqlite3session_memory_used"] = wasmExports["sqlite3session_memory_used"]; - Module["_sqlite3session_object_config"] = wasmExports["sqlite3session_object_config"]; - Module["_sqlite3session_changeset_size"] = wasmExports["sqlite3session_changeset_size"]; - Module["_sqlite3changeset_start"] = wasmExports["sqlite3changeset_start"]; - Module["_sqlite3changeset_start_v2"] = wasmExports["sqlite3changeset_start_v2"]; - Module["_sqlite3changeset_start_strm"] = wasmExports["sqlite3changeset_start_strm"]; - Module["_sqlite3changeset_start_v2_strm"] = wasmExports["sqlite3changeset_start_v2_strm"]; - Module["_sqlite3changeset_next"] = wasmExports["sqlite3changeset_next"]; - Module["_sqlite3changeset_op"] = wasmExports["sqlite3changeset_op"]; - Module["_sqlite3changeset_pk"] = wasmExports["sqlite3changeset_pk"]; - Module["_sqlite3changeset_old"] = wasmExports["sqlite3changeset_old"]; - Module["_sqlite3changeset_new"] = wasmExports["sqlite3changeset_new"]; - Module["_sqlite3changeset_conflict"] = wasmExports["sqlite3changeset_conflict"]; - Module["_sqlite3changeset_fk_conflicts"] = wasmExports["sqlite3changeset_fk_conflicts"]; - Module["_sqlite3changeset_finalize"] = wasmExports["sqlite3changeset_finalize"]; - Module["_sqlite3changeset_invert"] = wasmExports["sqlite3changeset_invert"]; - Module["_sqlite3changeset_invert_strm"] = wasmExports["sqlite3changeset_invert_strm"]; - Module["_sqlite3changeset_apply_v2"] = wasmExports["sqlite3changeset_apply_v2"]; - Module["_sqlite3changeset_apply_v3"] = wasmExports["sqlite3changeset_apply_v3"]; - Module["_sqlite3changeset_apply"] = wasmExports["sqlite3changeset_apply"]; - Module["_sqlite3changeset_apply_v3_strm"] = wasmExports["sqlite3changeset_apply_v3_strm"]; - Module["_sqlite3changeset_apply_v2_strm"] = wasmExports["sqlite3changeset_apply_v2_strm"]; - Module["_sqlite3changeset_apply_strm"] = wasmExports["sqlite3changeset_apply_strm"]; - Module["_sqlite3changegroup_new"] = wasmExports["sqlite3changegroup_new"]; - Module["_sqlite3changegroup_add"] = wasmExports["sqlite3changegroup_add"]; - Module["_sqlite3changegroup_output"] = wasmExports["sqlite3changegroup_output"]; - Module["_sqlite3changegroup_add_strm"] = wasmExports["sqlite3changegroup_add_strm"]; - Module["_sqlite3changegroup_output_strm"] = wasmExports["sqlite3changegroup_output_strm"]; - Module["_sqlite3changegroup_delete"] = wasmExports["sqlite3changegroup_delete"]; - Module["_sqlite3changeset_concat"] = wasmExports["sqlite3changeset_concat"]; - Module["_sqlite3changeset_concat_strm"] = wasmExports["sqlite3changeset_concat_strm"]; - Module["_sqlite3session_config"] = wasmExports["sqlite3session_config"]; - Module["_sqlite3_sourceid"] = wasmExports["sqlite3_sourceid"]; - Module["_sqlite3__wasm_pstack_ptr"] = wasmExports["sqlite3__wasm_pstack_ptr"]; - Module["_sqlite3__wasm_pstack_restore"] = wasmExports["sqlite3__wasm_pstack_restore"]; - Module["_sqlite3__wasm_pstack_alloc"] = wasmExports["sqlite3__wasm_pstack_alloc"]; - Module["_sqlite3__wasm_pstack_remaining"] = wasmExports["sqlite3__wasm_pstack_remaining"]; - Module["_sqlite3__wasm_pstack_quota"] = wasmExports["sqlite3__wasm_pstack_quota"]; - Module["_sqlite3__wasm_test_struct"] = wasmExports["sqlite3__wasm_test_struct"]; - Module["_sqlite3__wasm_enum_json"] = wasmExports["sqlite3__wasm_enum_json"]; - Module["_sqlite3__wasm_vfs_unlink"] = wasmExports["sqlite3__wasm_vfs_unlink"]; - Module["_sqlite3__wasm_db_vfs"] = wasmExports["sqlite3__wasm_db_vfs"]; - Module["_sqlite3__wasm_db_reset"] = wasmExports["sqlite3__wasm_db_reset"]; - Module["_sqlite3__wasm_db_export_chunked"] = wasmExports["sqlite3__wasm_db_export_chunked"]; - Module["_sqlite3__wasm_db_serialize"] = wasmExports["sqlite3__wasm_db_serialize"]; - Module["_sqlite3__wasm_vfs_create_file"] = wasmExports["sqlite3__wasm_vfs_create_file"]; - Module["_sqlite3__wasm_posix_create_file"] = wasmExports["sqlite3__wasm_posix_create_file"]; - Module["_sqlite3__wasm_kvvfsMakeKey"] = wasmExports["sqlite3__wasm_kvvfsMakeKey"]; - Module["_sqlite3__wasm_kvvfs_methods"] = wasmExports["sqlite3__wasm_kvvfs_methods"]; - Module["_sqlite3__wasm_vtab_config"] = wasmExports["sqlite3__wasm_vtab_config"]; - Module["_sqlite3__wasm_db_config_ip"] = wasmExports["sqlite3__wasm_db_config_ip"]; - Module["_sqlite3__wasm_db_config_pii"] = wasmExports["sqlite3__wasm_db_config_pii"]; - Module["_sqlite3__wasm_db_config_s"] = wasmExports["sqlite3__wasm_db_config_s"]; - Module["_sqlite3__wasm_config_i"] = wasmExports["sqlite3__wasm_config_i"]; - Module["_sqlite3__wasm_config_ii"] = wasmExports["sqlite3__wasm_config_ii"]; - Module["_sqlite3__wasm_config_j"] = wasmExports["sqlite3__wasm_config_j"]; - Module["_sqlite3__wasm_qfmt_token"] = wasmExports["sqlite3__wasm_qfmt_token"]; - Module["_sqlite3__wasm_kvvfs_decode"] = wasmExports["sqlite3__wasm_kvvfs_decode"]; - Module["_sqlite3__wasm_kvvfs_encode"] = wasmExports["sqlite3__wasm_kvvfs_encode"]; - Module["_sqlite3__wasm_init_wasmfs"] = wasmExports["sqlite3__wasm_init_wasmfs"]; - Module["_sqlite3__wasm_test_intptr"] = wasmExports["sqlite3__wasm_test_intptr"]; - Module["_sqlite3__wasm_test_voidptr"] = wasmExports["sqlite3__wasm_test_voidptr"]; - Module["_sqlite3__wasm_test_int64_max"] = wasmExports["sqlite3__wasm_test_int64_max"]; - Module["_sqlite3__wasm_test_int64_min"] = wasmExports["sqlite3__wasm_test_int64_min"]; - Module["_sqlite3__wasm_test_int64_times2"] = wasmExports["sqlite3__wasm_test_int64_times2"]; - Module["_sqlite3__wasm_test_int64_minmax"] = wasmExports["sqlite3__wasm_test_int64_minmax"]; - Module["_sqlite3__wasm_test_int64ptr"] = wasmExports["sqlite3__wasm_test_int64ptr"]; - Module["_sqlite3__wasm_test_stack_overflow"] = wasmExports["sqlite3__wasm_test_stack_overflow"]; - Module["_sqlite3__wasm_test_str_hello"] = wasmExports["sqlite3__wasm_test_str_hello"]; - Module["_sqlite3__wasm_SQLTester_strglob"] = wasmExports["sqlite3__wasm_SQLTester_strglob"]; - Module["_malloc"] = wasmExports["malloc"]; - Module["_free"] = wasmExports["free"]; - Module["_realloc"] = wasmExports["realloc"]; - _emscripten_builtin_memalign = wasmExports["emscripten_builtin_memalign"]; - wasmExports["_emscripten_stack_restore"]; - wasmExports["_emscripten_stack_alloc"]; - wasmExports["emscripten_stack_get_current"]; - wasmExports["__indirect_function_table"]; - } - var wasmImports = { - __syscall_chmod: ___syscall_chmod, - __syscall_faccessat: ___syscall_faccessat, - __syscall_fchmod: ___syscall_fchmod, - __syscall_fchown32: ___syscall_fchown32, - __syscall_fcntl64: ___syscall_fcntl64, - __syscall_fstat64: ___syscall_fstat64, - __syscall_ftruncate64: ___syscall_ftruncate64, - __syscall_getcwd: ___syscall_getcwd, - __syscall_ioctl: ___syscall_ioctl, - __syscall_lstat64: ___syscall_lstat64, - __syscall_mkdirat: ___syscall_mkdirat, - __syscall_newfstatat: ___syscall_newfstatat, - __syscall_openat: ___syscall_openat, - __syscall_readlinkat: ___syscall_readlinkat, - __syscall_rmdir: ___syscall_rmdir, - __syscall_stat64: ___syscall_stat64, - __syscall_unlinkat: ___syscall_unlinkat, - __syscall_utimensat: ___syscall_utimensat, - _localtime_js: __localtime_js, - _mmap_js: __mmap_js, - _munmap_js: __munmap_js, - _tzset_js: __tzset_js, - clock_time_get: _clock_time_get, - emscripten_date_now: _emscripten_date_now, - emscripten_get_heap_max: _emscripten_get_heap_max, - emscripten_get_now: _emscripten_get_now, - emscripten_resize_heap: _emscripten_resize_heap, - environ_get: _environ_get, - environ_sizes_get: _environ_sizes_get, - fd_close: _fd_close, - fd_fdstat_get: _fd_fdstat_get, - fd_read: _fd_read, - fd_seek: _fd_seek, - fd_sync: _fd_sync, - fd_write: _fd_write, - memory: wasmMemory - }; - function run() { - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } - preRun(); - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } - function doRun() { - Module["calledRun"] = true; - if (ABORT) return; - initRuntime(); - readyPromiseResolve?.(Module); - Module["onRuntimeInitialized"]?.(); - postRun(); - } - if (Module["setStatus"]) { - Module["setStatus"]("Running..."); - setTimeout(() => { - setTimeout(() => Module["setStatus"](""), 1); - doRun(); - }, 1); - } else doRun(); - } - var wasmExports = await createWasm(); - run(); - /** - post-js-header.js is to be prepended to other code to create - post-js.js for use with Emscripten's --post-js flag, so it gets - injected in the earliest stages of sqlite3InitModule(). - - Running this function will bootstrap the library and return - a Promise to the sqlite3 namespace object. - - In the canonical builds, this gets called by extern-post-js.c-pp.js - */ - Module.runSQLite3PostLoadInit = async function(sqlite3InitScriptInfo, EmscriptenModule, sqlite3IsUnderTest) { - /** ^^^ Don't use Module.postRun, as that runs a different time - depending on whether this file is built with emcc 3.1.x or - 4.0.x. This function name is intentionally obnoxiously verbose to - ensure that we don't collide with current and future Emscripten - symbol names. */ - "use strict"; - delete EmscriptenModule.runSQLite3PostLoadInit; - globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(apiConfig = globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) { - if (sqlite3ApiBootstrap.sqlite3) { - (sqlite3ApiBootstrap.sqlite3.config || console).warn("sqlite3ApiBootstrap() called multiple times.", "Config and external initializers are ignored on calls after the first."); - return sqlite3ApiBootstrap.sqlite3; - } - const config = Object.assign(Object.create(null), { - exports: void 0, - memory: void 0, - bigIntEnabled: !!globalThis.BigInt64Array, - debug: console.debug.bind(console), - warn: console.warn.bind(console), - error: console.error.bind(console), - log: console.log.bind(console), - wasmfsOpfsDir: "/opfs", - useStdAlloc: false - }, apiConfig || {}); - Object.assign(config, { - allocExportName: config.useStdAlloc ? "malloc" : "sqlite3_malloc", - deallocExportName: config.useStdAlloc ? "free" : "sqlite3_free", - reallocExportName: config.useStdAlloc ? "realloc" : "sqlite3_realloc" - }); - [ - "exports", - "memory", - "functionTable", - "wasmfsOpfsDir" - ].forEach((k) => { - if ("function" === typeof config[k]) config[k] = config[k](); - }); - /** - The main sqlite3 binding API gets installed into this object, - mimicking the C API as closely as we can. The numerous members - names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as - possible, identically to the C-native counterparts, as documented at: - - https://sqlite.org/c3ref/intro.html - - A very few exceptions require an additional level of proxy - function or may otherwise require special attention in the WASM - environment, and all such cases are documented somewhere below - in this file or in sqlite3-api-glue.js. capi members which are - not documented are installed as 1-to-1 proxies for their - C-side counterparts. - */ - const capi = Object.create(null); - /** - Holds state which are specific to the WASM-related - infrastructure and glue code. - - Note that a number of members of this object are injected - dynamically after the api object is fully constructed, so - not all are documented in this file. - */ - const wasm = Object.create(null); - /** Internal helper for SQLite3Error ctor. */ - const __rcStr = (rc) => { - return capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc) || "Unknown result code #" + rc; - }; - /** Internal helper for SQLite3Error ctor. */ - const isInt32 = (n) => "number" === typeof n && n === (n | 0) && n <= 2147483647 && n >= -2147483648; - /** - An Error subclass specifically for reporting DB-level errors and - enabling clients to unambiguously identify such exceptions. - The C-level APIs never throw, but some of the higher-level - C-style APIs do and the object-oriented APIs use exceptions - exclusively to report errors. - */ - class SQLite3Error extends Error { - /** - Constructs this object with a message depending on its arguments: - - If its first argument is an integer, it is assumed to be - an SQLITE_... result code and it is passed to - sqlite3.capi.sqlite3_js_rc_str() to stringify it. - - If called with exactly 2 arguments and the 2nd is an object, - that object is treated as the 2nd argument to the parent - constructor. - - The exception's message is created by concatenating its - arguments with a space between each, except for the - two-args-with-an-object form and that the first argument will - get coerced to a string, as described above, if it's an - integer. - - If passed an integer first argument, the error object's - `resultCode` member will be set to the given integer value, - else it will be set to capi.SQLITE_ERROR. - */ - constructor(...args) { - let rc; - if (args.length) if (isInt32(args[0])) { - rc = args[0]; - if (1 === args.length) super(__rcStr(args[0])); - else { - const rcStr = __rcStr(rc); - if ("object" === typeof args[1]) super(rcStr, args[1]); - else { - args[0] = rcStr + ":"; - super(args.join(" ")); - } - } - } else if (2 === args.length && "object" === typeof args[1]) super(...args); - else super(args.join(" ")); - this.resultCode = rc || capi.SQLITE_ERROR; - this.name = "SQLite3Error"; - } - } - /** - Functionally equivalent to the SQLite3Error constructor but may - be used as part of an expression, e.g.: - - ``` - return someFunction(x) || SQLite3Error.toss(...); - ``` - */ - SQLite3Error.toss = (...args) => { - throw new SQLite3Error(...args); - }; - const toss3 = SQLite3Error.toss; - if (config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)) toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'."); - /** - Returns true if the given BigInt value is small enough to fit - into an int64 value, else false. - */ - const bigIntFits64 = function f(b) { - if (!f._max) { - f._max = BigInt("0x7fffffffffffffff"); - f._min = ~f._max; - } - return b >= f._min && b <= f._max; - }; - /** - Returns true if the given BigInt value is small enough to fit - into an int32, else false. - */ - const bigIntFits32 = (b) => b >= -2147483647n - 1n && b <= 2147483647n; - /** - Returns true if the given BigInt value is small enough to fit - into a double value without loss of precision, else false. - */ - const bigIntFitsDouble = function f(b) { - if (!f._min) { - f._min = Number.MIN_SAFE_INTEGER; - f._max = Number.MAX_SAFE_INTEGER; - } - return b >= f._min && b <= f._max; - }; - /** Returns v if v appears to be a TypedArray, else false. */ - const isTypedArray = (v) => { - return v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT) ? v : false; - }; - /** - Returns true if v appears to be one of our bind()-able TypedArray - types: Uint8Array or Int8Array or ArrayBuffer. Support for - TypedArrays with element sizes >1 is a potential TODO just - waiting on a use case to justify them. Until then, their `buffer` - property can be used to pass them as an ArrayBuffer. If it's not - a bindable array type, a falsy value is returned. - */ - const isBindableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); - /** - Returns true if v appears to be one of the TypedArray types - which is legal for holding SQL code (as opposed to binary blobs). - - Currently this is the same as isBindableTypedArray() but it - seems likely that we'll eventually want to add Uint32Array - and friends to the isBindableTypedArray() list but not to the - isSQLableTypedArray() list. - */ - const isSQLableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer); - /** Returns true if isBindableTypedArray(v) does, else throws with a message - that v is not a supported TypedArray value. */ - const affirmBindableTypedArray = (v) => isBindableTypedArray(v) || toss3("Value is not of a supported TypedArray type."); - /** - If v is-a Array, its join("") result is returned. If - isSQLableTypedArray(v) is true then wasm.typedArrayToString(v) is - returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is - returned. Else v is returned as-is. - - Reminder to self: the "return as-is" instead of returning ''+v is - arguably a design mistake but changing it is risky at this point. - */ - const flexibleString = function(v) { - if (isSQLableTypedArray(v)) return wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v, 0, v.length); - else if (Array.isArray(v)) return v.join(""); - else if (wasm.isPtr(v)) v = wasm.cstrToJs(v); - return v; - }; - /** - An Error subclass specifically for reporting Wasm-level malloc() - failure and enabling clients to unambiguously identify such - exceptions. - */ - class WasmAllocError extends Error { - /** - If called with 2 arguments and the 2nd one is an object, it - behaves like the Error constructor, else it concatenates all - arguments together with a single space between each to - construct an error message string. As a special case, if - called with no arguments then it uses a default error - message. - */ - constructor(...args) { - if (2 === args.length && "object" === typeof args[1]) super(...args); - else if (args.length) super(args.join(" ")); - else super("Allocation failed."); - this.resultCode = capi.SQLITE_NOMEM; - this.name = "WasmAllocError"; - } - } - /** - Functionally equivalent to the WasmAllocError constructor but may - be used as part of an expression, e.g.: - - ``` - return someAllocatingFunction(x) || WasmAllocError.toss(...); - ``` - */ - WasmAllocError.toss = (...args) => { - throw new WasmAllocError(...args); - }; - Object.assign(capi, { - sqlite3_bind_blob: void 0, - sqlite3_bind_text: void 0, - sqlite3_create_function_v2: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) => {}, - sqlite3_create_function: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) => {}, - sqlite3_create_window_function: (pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) => {}, - sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags, stmtPtrPtr, strPtrPtr) => {}, - sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen, stmtPtrPtr, strPtrPtr) => {}, - sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg) => {}, - sqlite3_randomness: (n, outPtr) => {} - }); - /** - Various internal-use utilities are added here as needed. They - are bound to an object only so that we have access to them in - the differently-scoped steps of the API bootstrapping - process. At the end of the API setup process, this object gets - removed. These are NOT part of the public API. - */ - const util = { - affirmBindableTypedArray, - flexibleString, - bigIntFits32, - bigIntFits64, - bigIntFitsDouble, - isBindableTypedArray, - isInt32, - isSQLableTypedArray, - isTypedArray, - isUIThread: () => globalThis.window === globalThis && !!globalThis.document, - toss: function(...args) { - throw new Error(args.join(" ")); - }, - toss3, - typedArrayPart: wasm.typedArrayPart, - assert: function(arg, msg) { - if (!arg) util.toss("Assertion failed:", msg); - }, - affirmDbHeader: function(bytes) { - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - const header = "SQLite format 3"; - if (15 > bytes.byteLength) toss3("Input does not contain an SQLite3 database header."); - for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss3("Input does not contain an SQLite3 database header."); - }, - affirmIsDb: function(bytes) { - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - const n = bytes.byteLength; - if (n < 512 || n % 512 !== 0) toss3("Byte array size", n, "is invalid for an SQLite3 db."); - util.affirmDbHeader(bytes); - } - }; - /** - wasm.X properties which are used for configuring the wasm - environment via whwashutil.js. This object gets fleshed out with - a number of WASM-specific utilities, in sqlite3-api-glue.c-pp.js. - */ - Object.assign(wasm, { - exports: config.exports || toss3("Missing API config.exports (WASM module exports)."), - memory: config.memory || config.exports["memory"] || toss3("API config object requires a WebAssembly.Memory object", "in either config.exports.memory (exported)", "or config.memory (imported)."), - pointerSize: "number" === typeof config.exports.sqlite3_libversion() ? 4 : 8, - bigIntEnabled: !!config.bigIntEnabled, - functionTable: config.functionTable, - alloc: void 0, - realloc: void 0, - dealloc: void 0 - }); - /** - wasm.alloc()'s srcTypedArray.byteLength bytes, - populates them with the values from the source - TypedArray, and returns the pointer to that memory. The - returned pointer must eventually be passed to - wasm.dealloc() to clean it up. - - The argument may be a Uint8Array, Int8Array, or ArrayBuffer, - and it throws if passed any other type. - - As a special case, to avoid further special cases where - this is used, if srcTypedArray.byteLength is 0, it - allocates a single byte and sets it to the value - 0. Even in such cases, calls must behave as if the - allocated memory has exactly srcTypedArray.byteLength - bytes. - */ - wasm.allocFromTypedArray = function(srcTypedArray) { - if (srcTypedArray instanceof ArrayBuffer) srcTypedArray = new Uint8Array(srcTypedArray); - affirmBindableTypedArray(srcTypedArray); - const pRet = wasm.alloc(srcTypedArray.byteLength || 1); - wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], Number(pRet)); - return pRet; - }; - { - const keyAlloc = config.allocExportName, keyDealloc = config.deallocExportName, keyRealloc = config.reallocExportName; - for (const key of [ - keyAlloc, - keyDealloc, - keyRealloc - ]) if (!(wasm.exports[key] instanceof Function)) toss3("Missing required exports[", key, "] function."); - wasm.alloc = function f(n) { - return f.impl(n) || WasmAllocError.toss("Failed to allocate", n, " bytes."); - }; - wasm.alloc.impl = wasm.exports[keyAlloc]; - wasm.realloc = function f(m, n) { - const m2 = f.impl(wasm.ptr.coerce(m), n); - return n ? m2 || WasmAllocError.toss("Failed to reallocate", n, " bytes.") : wasm.ptr.null; - }; - wasm.realloc.impl = wasm.exports[keyRealloc]; - wasm.dealloc = function f(m) { - f.impl(wasm.ptr.coerce(m)); - }; - wasm.dealloc.impl = wasm.exports[keyDealloc]; - } - /** - Reports info about compile-time options using - sqlite3_compileoption_get() and sqlite3_compileoption_used(). It - has several distinct uses: - - If optName is an array then it is expected to be a list of - compilation options and this function returns an object - which maps each such option to true or false, indicating - whether or not the given option was included in this - build. That object is returned. - - If optName is an object, its keys are expected to be compilation - options and this function sets each entry to true or false, - indicating whether the compilation option was used or not. That - object is returned. - - If passed no arguments then it returns an object mapping - all known compilation options to their compile-time values, - or boolean true if they are defined with no value. This - result, which is relatively expensive to compute, is cached - and returned for future no-argument calls. - - In all other cases it returns true if the given option was - active when when compiling the sqlite3 module, else false. - - Compile-time option names may optionally include their - "SQLITE_" prefix. When it returns an object of all options, - the prefix is elided. - */ - wasm.compileOptionUsed = function f(optName) { - if (!arguments.length) { - if (f._result) return f._result; - else if (!f._opt) { - f._rx = /^([^=]+)=(.+)/; - f._rxInt = /^-?\d+$/; - f._opt = function(opt, rv) { - const m = f._rx.exec(opt); - rv[0] = m ? m[1] : opt; - rv[1] = m ? f._rxInt.test(m[2]) ? +m[2] : m[2] : true; - }; - } - const rc = Object.create(null), ov = [0, 0]; - let i = 0, k; - while (k = capi.sqlite3_compileoption_get(i++)) { - f._opt(k, ov); - rc[ov[0]] = ov[1]; - } - return f._result = rc; - } else if (Array.isArray(optName)) { - const rc = Object.create(null); - optName.forEach((v) => { - rc[v] = capi.sqlite3_compileoption_used(v); - }); - return rc; - } else if ("object" === typeof optName) { - Object.keys(optName).forEach((k) => { - optName[k] = capi.sqlite3_compileoption_used(k); - }); - return optName; - } - return "string" === typeof optName ? !!capi.sqlite3_compileoption_used(optName) : false; - }; - /** - sqlite3.wasm.pstack (pseudo-stack) holds a special-case allocator - intended solely for short-lived, small data. In practice, it's - primarily used to allocate output pointers. It must not be used - for any memory which needs to outlive the scope in which it's - obtained from pstack. - - The library guarantees only that a minimum of 2kb are available - in this allocator, and it may provide more (it's a build-time - value). pstack.quota and pstack.remaining can be used to get the - total resp. remaining amount of memory. - - It has only a single intended usage pattern: - - ``` - const stackPos = pstack.pointer; - try{ - const ptr = pstack.alloc(8); - // ==> pstack.pointer === ptr - const otherPtr = pstack.alloc(8); - // ==> pstack.pointer === otherPtr - ... - }finally{ - pstack.restore(stackPos); - // ==> pstack.pointer === stackPos - } - ``` - - This allocator is much faster than a general-purpose one but is - limited to usage patterns like the one shown above (which are - pretty common when using sqlite3.capi). - - The memory lives in the WASM heap and can be used with routines - such as wasm.poke() and wasm.heap8u().slice(). - */ - wasm.pstack = Object.assign(Object.create(null), { - restore: wasm.exports.sqlite3__wasm_pstack_restore, - alloc: function(n) { - if ("string" === typeof n && !(n = wasm.sizeofIR(n))) WasmAllocError.toss("Invalid value for pstack.alloc(", arguments[0], ")"); - return wasm.exports.sqlite3__wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate", n, "bytes from the pstack."); - }, - allocChunks: function(n, sz) { - if ("string" === typeof sz && !(sz = wasm.sizeofIR(sz))) WasmAllocError.toss("Invalid size value for allocChunks(", arguments[1], ")"); - const mem = wasm.pstack.alloc(n * sz); - const rc = [mem]; - let i = 1, offset = sz; - for (; i < n; ++i, offset += sz) rc.push(wasm.ptr.add(mem, offset)); - return rc; - }, - allocPtr: (n = 1, safePtrSize = true) => { - return 1 === n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptr.size) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptr.size); - }, - call: function(f) { - const stackPos = wasm.pstack.pointer; - try { - return f(sqlite3); - } finally { - wasm.pstack.restore(stackPos); - } - } - }); - Object.defineProperties(wasm.pstack, { - pointer: { - configurable: false, - iterable: true, - writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_ptr - }, - quota: { - configurable: false, - iterable: true, - writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_quota - }, - remaining: { - configurable: false, - iterable: true, - writeable: false, - get: wasm.exports.sqlite3__wasm_pstack_remaining - } - }); - /** - Docs: https://sqlite.org/wasm/doc/trunk/api-c-style.md#sqlite3_randomness - */ - capi.sqlite3_randomness = (...args) => { - if (1 === args.length && util.isTypedArray(args[0]) && 1 === args[0].BYTES_PER_ELEMENT) { - const ta = args[0]; - if (0 === ta.byteLength) { - wasm.exports.sqlite3_randomness(0, wasm.ptr.null); - return ta; - } - const stack = wasm.pstack.pointer; - try { - let n = ta.byteLength, offset = 0; - const r = wasm.exports.sqlite3_randomness; - const heap = wasm.heap8u(); - const nAlloc = n < 512 ? n : 512; - const ptr = wasm.pstack.alloc(nAlloc); - do { - const j = n > nAlloc ? nAlloc : n; - r(j, ptr); - ta.set(wasm.typedArrayPart(heap, ptr, wasm.ptr.add(ptr, j)), offset); - n -= j; - offset += j; - } while (n > 0); - } catch (e) { - config.error("Highly unexpected (and ignored!) exception in sqlite3_randomness():", e); - } finally { - wasm.pstack.restore(stack); - } - return ta; - } - wasm.exports.sqlite3_randomness(...args); - }; - /** - If the wasm environment has a WASMFS/OPFS-backed persistent - storage directory, its path is returned by this function. If it - does not then it returns "" (noting that "" is a falsy value). - - The first time this is called, this function inspects the current - environment to determine whether WASMFS persistence support is - available and, if it is, enables it (if needed). After the first - call it always returns the cached result. - - If the returned string is not empty, any files stored under the - returned path (recursively) are housed in OPFS storage. If the - returned string is empty, this particular persistent storage - option is not available on the client. - - Though the mount point name returned by this function is intended - to remain stable, clients should not hard-coded it anywhere. - Always call this function to get the path. - - This function is a no-op in most builds of this library, as the - WASMFS capability requires a custom build. - */ - capi.sqlite3_wasmfs_opfs_dir = function() { - if (void 0 !== this.dir) return this.dir; - const pdir = config.wasmfsOpfsDir; - if (!pdir || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !wasm.exports.sqlite3__wasm_init_wasmfs) return this.dir = ""; - try { - if (pdir && 0 === wasm.xCallWrapped("sqlite3__wasm_init_wasmfs", "i32", ["string"], pdir)) return this.dir = pdir; - else return this.dir = ""; - } catch (e) { - return this.dir = ""; - } - }.bind(Object.create(null)); - /** - Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a - non-empty string and the given name starts with (that string + - '/'), else returns false. - */ - capi.sqlite3_wasmfs_filename_is_persistent = function(name) { - const p = capi.sqlite3_wasmfs_opfs_dir(); - return p && name ? name.startsWith(p + "/") : false; - }; - /** - Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name - (defaulting to "main"), returns a truthy value (see below) if - that db uses that VFS, else returns false. If pDb is falsy then - the 3rd argument is ignored and this function returns a truthy - value if the default VFS name matches that of the 2nd argument. - Results are undefined if pDb is truthy but refers to an invalid - pointer. The 3rd argument specifies the database name of the - given database connection to check, defaulting to the main db. - - The 2nd and 3rd arguments may either be a JS string or a WASM - C-string. If the 2nd argument is a NULL WASM pointer, the default - VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is - assumed. - - The truthy value it returns is a pointer to the `sqlite3_vfs` - object. - - To permit safe use of this function from APIs which may be called - via C (like SQL UDFs), this function does not throw: if bad - arguments cause a conversion error when passing into wasm-space, - false is returned. - */ - capi.sqlite3_js_db_uses_vfs = function(pDb, vfsName, dbName = 0) { - try { - const pK = capi.sqlite3_vfs_find(vfsName); - if (!pK) return false; - else if (!pDb) return pK === capi.sqlite3_vfs_find(0) ? pK : false; - else return pK === capi.sqlite3_js_db_vfs(pDb, dbName) ? pK : false; - } catch (e) { - return false; - } - }; - /** - Returns an array of the names of all currently-registered sqlite3 - VFSes. - */ - capi.sqlite3_js_vfs_list = function() { - const rc = []; - let pVfs = capi.sqlite3_vfs_find(wasm.ptr.null); - while (pVfs) { - const oVfs = new capi.sqlite3_vfs(pVfs); - rc.push(wasm.cstrToJs(oVfs.$zName)); - pVfs = oVfs.$pNext; - oVfs.dispose(); - } - return rc; - }; - /** - A convenience wrapper around sqlite3_serialize() which serializes - the given `sqlite3*` pointer to a Uint8Array. The first argument - may be either an `sqlite3*` or an sqlite3.oo1.DB instance. - - On success it returns a Uint8Array. If the schema is empty, an - empty array is returned. - - `schema` is the schema to serialize. It may be a WASM C-string - pointer or a JS string. If it is falsy, it defaults to `"main"`. - - On error it throws with a description of the problem. - */ - capi.sqlite3_js_db_export = function(pDb, schema = 0) { - pDb = wasm.xWrap.testConvertArg("sqlite3*", pDb); - if (!pDb) toss3("Invalid sqlite3* argument."); - if (!wasm.bigIntEnabled) toss3("BigInt support is not enabled."); - const scope = wasm.scopedAllocPush(); - let pOut; - try { - const pSize = wasm.scopedAlloc(8 + wasm.ptr.size); - const ppOut = wasm.ptr.add(pSize, 8); - /** - Maintenance reminder, since this cost a full hour of grief - and confusion: if the order of pSize/ppOut are reversed in - that memory block, fetching the value of pSize after the - export reads a garbage size because it's not on an 8-byte - memory boundary! - */ - const zSchema = schema ? wasm.isPtr(schema) ? schema : wasm.scopedAllocCString("" + schema) : wasm.ptr.null; - let rc = wasm.exports.sqlite3__wasm_db_serialize(pDb, zSchema, ppOut, pSize, 0); - if (rc) toss3("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc)); - pOut = wasm.peekPtr(ppOut); - const nOut = wasm.peek(pSize, "i64"); - rc = nOut ? wasm.heap8u().slice(Number(pOut), Number(pOut) + Number(nOut)) : new Uint8Array(); - return rc; - } finally { - if (pOut) wasm.exports.sqlite3_free(pOut); - wasm.scopedAllocPop(scope); - } - }; - /** - Given a `sqlite3*` and a database name (JS string or WASM - C-string pointer, which may be 0), returns a pointer to the - sqlite3_vfs responsible for it. If the given db name is null/0, - or not provided, then "main" is assumed. - */ - capi.sqlite3_js_db_vfs = (dbPointer, dbName = wasm.ptr.null) => util.sqlite3__wasm_db_vfs(dbPointer, dbName); - /** - A thin wrapper around capi.sqlite3_aggregate_context() which - behaves the same except that it throws a WasmAllocError if that - function returns 0. As a special case, if n is falsy it does - _not_ throw if that function returns 0. That special case is - intended for use with xFinal() implementations. - */ - capi.sqlite3_js_aggregate_context = (pCtx, n) => { - return capi.sqlite3_aggregate_context(pCtx, n) || (n ? WasmAllocError.toss("Cannot allocate", n, "bytes for sqlite3_aggregate_context()") : 0); - }; - /** - If the current environment supports the POSIX file APIs, this routine - creates (or overwrites) the given file using those APIs. This is - primarily intended for use in Emscripten-based builds where the POSIX - APIs are transparently proxied by an in-memory virtual filesystem. - It may behave differently in other environments. - - The first argument must be either a JS string or WASM C-string - holding the filename. This routine does _not_ create intermediary - directories if the filename has a directory part. - - The 2nd argument may either a valid WASM memory pointer, an - ArrayBuffer, or a Uint8Array. The 3rd must be the length, in - bytes, of the data array to copy. If the 2nd argument is an - ArrayBuffer or Uint8Array and the 3rd is not a positive integer - then the 3rd defaults to the array's byteLength value. - - Results are undefined if data is a WASM pointer and dataLen is - exceeds data's bounds. - - Throws if any arguments are invalid or if creating or writing to - the file fails. - - Added in 3.43 as an alternative for the deprecated - sqlite3_js_vfs_create_file(). - */ - capi.sqlite3_js_posix_create_file = function(filename, data, dataLen) { - let pData; - if (data && wasm.isPtr(data)) pData = data; - else if (data instanceof ArrayBuffer || data instanceof Uint8Array) { - pData = wasm.allocFromTypedArray(data); - if (arguments.length < 3 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; - } else SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file()."); - try { - if (!util.isInt32(dataLen) || dataLen < 0) SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file()."); - const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen); - if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); - } finally { - if (pData && pData !== data) wasm.dealloc(pData); - } - }; - /** - Deprecation warning: this function does not work properly in - debug builds of sqlite3 because its out-of-scope use of the - sqlite3_vfs API triggers assertions in the core library. That - was unfortunately not discovered until 2023-08-11. This function - is now deprecated. It should not be used in new code and should - be removed from existing code. - - Alternative options: - - - The "unix" VFS and its variants can get equivalent - functionality with sqlite3_js_posix_create_file(). - - - OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs" - VFS, or the importDb() method of the PoolUtil object provided - by the "opfs-sahpool" OPFS (noting that its VFS name may differ - depending on client-side configuration). We cannot proxy those - from here because the former is necessarily asynchronous and - the latter requires information not available to this function. - - Historical (deprecated) behaviour: - - Creates a file using the storage appropriate for the given - sqlite3_vfs. The first argument may be a VFS name (JS string - only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or - a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the - default VFS. If passed a string which does not resolve using - sqlite3_vfs_find(), an exception is thrown. (Note that a WASM - C-string is not accepted because it is impossible to - distinguish from a C-level `sqlite3_vfs*`.) - - The second argument, the filename, must be a JS or WASM C-string. - - The 3rd may either be falsy, a valid WASM memory pointer, an - ArrayBuffer, or a Uint8Array. The 4th must be the length, in - bytes, of the data array to copy. If the 3rd argument is an - ArrayBuffer or Uint8Array and the 4th is not a positive integer - then the 4th defaults to the array's byteLength value. - - If data is falsy then a file is created with dataLen bytes filled - with uninitialized data (whatever truncate() leaves there). If - data is not falsy then a file is created or truncated and it is - filled with the first dataLen bytes of the data source. - - Throws if any arguments are invalid or if creating or writing to - the file fails. - - Note that most VFSes do _not_ automatically create directory - parts of filenames, nor do all VFSes have a concept of - directories. If the given filename is not valid for the given - VFS, an exception will be thrown. This function exists primarily - to assist in implementing file-upload capability, with the caveat - that clients must have some idea of the VFS into which they want - to upload and that VFS must support the operation. - - VFS-specific notes: - - - "memdb": results are undefined. - - - "kvvfs": will fail with an I/O error due to strict internal - requirements of that VFS's xTruncate(). - - - "unix" and related: will use the WASM build's equivalent of the - POSIX I/O APIs. This will work so long as neither a specific - VFS nor the WASM environment imposes requirements which break - it. (Much later: it turns out that debug builds of the library - impose such requirements, in that they assert() that dataLen is - an even multiple of a valid db page size.) - - - "opfs": uses OPFS storage and creates directory parts of the - filename. It can only be used to import an SQLite3 database - file and will fail if given anything else. - */ - capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen) { - config.warn("sqlite3_js_vfs_create_file() is deprecated and", "should be avoided because it can lead to C-level crashes.", "See its documentation for alternatives."); - let pData; - if (data) if (wasm.isPtr(data)) pData = data; - else { - if (data instanceof ArrayBuffer) data = new Uint8Array(data); - if (data instanceof Uint8Array) { - pData = wasm.allocFromTypedArray(data); - if (arguments.length < 4 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength; - } else SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file()."); - } - else pData = 0; - if (!util.isInt32(dataLen) || dataLen < 0) { - if (pData && pData !== data) wasm.dealloc(pData); - SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file()."); - } - try { - const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen); - if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc)); - } finally { - if (pData && pData !== data) wasm.dealloc(pData); - } - }; - /** - Converts SQL input from a variety of convenient formats - to plain strings. - - If v is a string, it is returned as-is. If it is-a Array, its - join("") result is returned. If is is a Uint8Array, Int8Array, - or ArrayBuffer, it is assumed to hold UTF-8-encoded text and is - decoded to a string. If it looks like a WASM pointer, - wasm.cstrToJs(sql) is returned. Else undefined is returned. - - Added in 3.44 - */ - capi.sqlite3_js_sql_to_string = (sql) => { - if ("string" === typeof sql) return sql; - const x = flexibleString(v); - return x === v ? void 0 : x; - }; - /** - Wraps all known variants of the C-side variadic - sqlite3_db_config(). - - Full docs: https://sqlite.org/c3ref/db_config.html - - Returns capi.SQLITE_MISUSE if op is not a valid operation ID. - - The variants which take `(int, int*)` arguments treat a - missing or falsy pointer argument as 0. - */ - capi.sqlite3_db_config = function(pDb, op, ...args) { - switch (op) { - case capi.SQLITE_DBCONFIG_ENABLE_FKEY: - case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: - case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: - case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: - case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: - case capi.SQLITE_DBCONFIG_ENABLE_QPSG: - case capi.SQLITE_DBCONFIG_TRIGGER_EQP: - case capi.SQLITE_DBCONFIG_RESET_DATABASE: - case capi.SQLITE_DBCONFIG_DEFENSIVE: - case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA: - case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: - case capi.SQLITE_DBCONFIG_DQS_DML: - case capi.SQLITE_DBCONFIG_DQS_DDL: - case capi.SQLITE_DBCONFIG_ENABLE_VIEW: - case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: - case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA: - case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS: - case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER: - case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: - case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: - case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: - if (!this.ip) this.ip = wasm.xWrap("sqlite3__wasm_db_config_ip", "int", [ - "sqlite3*", - "int", - "int", - "*" - ]); - return this.ip(pDb, op, args[0], args[1] || 0); - case capi.SQLITE_DBCONFIG_LOOKASIDE: - if (!this.pii) this.pii = wasm.xWrap("sqlite3__wasm_db_config_pii", "int", [ - "sqlite3*", - "int", - "*", - "int", - "int" - ]); - return this.pii(pDb, op, args[0], args[1], args[2]); - case capi.SQLITE_DBCONFIG_MAINDBNAME: - if (!this.s) this.s = wasm.xWrap("sqlite3__wasm_db_config_s", "int", [ - "sqlite3*", - "int", - "string:static" - ]); - return this.s(pDb, op, args[0]); - default: return capi.SQLITE_MISUSE; - } - }.bind(Object.create(null)); - /** - Given a (sqlite3_value*), this function attempts to convert it - to an equivalent JS value with as much fidelity as feasible and - return it. - - By default it throws if it cannot determine any sensible - conversion. If passed a falsy second argument, it instead returns - `undefined` if no suitable conversion is found. Note that there - is no conversion from SQL to JS which results in the `undefined` - value, so `undefined` has an unambiguous meaning here. It will - always throw a WasmAllocError if allocating memory for a - conversion fails. - - Caveats: - - - It does not support sqlite3_value_to_pointer() conversions - because those require a type name string which this function - does not have and cannot sensibly be given at the level of the - API where this is used (e.g. automatically converting UDF - arguments). Clients using sqlite3_value_to_pointer(), and its - related APIs, will need to manage those themselves. - */ - capi.sqlite3_value_to_js = function(pVal, throwIfCannotConvert = true) { - let arg; - const valType = capi.sqlite3_value_type(pVal); - switch (valType) { - case capi.SQLITE_INTEGER: - if (wasm.bigIntEnabled) { - arg = capi.sqlite3_value_int64(pVal); - if (util.bigIntFitsDouble(arg)) arg = Number(arg); - } else arg = capi.sqlite3_value_double(pVal); - break; - case capi.SQLITE_FLOAT: - arg = capi.sqlite3_value_double(pVal); - break; - case capi.SQLITE_TEXT: - arg = capi.sqlite3_value_text(pVal); - break; - case capi.SQLITE_BLOB: { - const n = capi.sqlite3_value_bytes(pVal); - const pBlob = capi.sqlite3_value_blob(pVal); - if (n && !pBlob) sqlite3.WasmAllocError.toss("Cannot allocate memory for blob argument of", n, "byte(s)"); - arg = n ? wasm.heap8u().slice(Number(pBlob), Number(pBlob) + Number(n)) : null; - break; - } - case capi.SQLITE_NULL: - arg = null; - break; - default: - if (throwIfCannotConvert) toss3(capi.SQLITE_MISMATCH, "Unhandled sqlite3_value_type():", valType); - arg = void 0; - } - return arg; - }; - /** - Requires a C-style array of `sqlite3_value*` objects and the - number of entries in that array. Returns a JS array containing - the results of passing each C array entry to - sqlite3_value_to_js(). The 3rd argument to this function is - passed on as the 2nd argument to that one. - */ - capi.sqlite3_values_to_js = function(argc, pArgv, throwIfCannotConvert = true) { - let i; - const tgt = []; - for (i = 0; i < argc; ++i) - /** - Curiously: despite ostensibly requiring 8-byte - alignment, the pArgv array is parcelled into chunks of - 4 bytes (1 pointer each). The values those point to - have 8-byte alignment but the individual argv entries - do not. - */ - tgt.push(capi.sqlite3_value_to_js(wasm.peekPtr(wasm.ptr.add(pArgv, wasm.ptr.size * i)), throwIfCannotConvert)); - return tgt; - }; - /** - Calls either sqlite3_result_error_nomem(), if e is-a - WasmAllocError, or sqlite3_result_error(). In the latter case, - the second argument is coerced to a string to create the error - message. - - The first argument is a (sqlite3_context*). Returns void. - Does not throw. - */ - capi.sqlite3_result_error_js = function(pCtx, e) { - if (e instanceof WasmAllocError) capi.sqlite3_result_error_nomem(pCtx); - else capi.sqlite3_result_error(pCtx, "" + e, -1); - }; - /** - This function passes its 2nd argument to one of the - sqlite3_result_xyz() routines, depending on the type of that - argument: - - - If (val instanceof Error), this function passes it to - sqlite3_result_error_js(). - - `null`: `sqlite3_result_null()` - - `boolean`: `sqlite3_result_int()` with a value of 0 or 1. - - `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or - `sqlite3_result_double()`, depending on the range of the number - and whether or not int64 support is enabled. - - `bigint`: similar to `number` but will trigger an error if the - value is too big to store in an int64. - - `string`: `sqlite3_result_text()` - - Uint8Array or Int8Array or ArrayBuffer: `sqlite3_result_blob()` - - `undefined`: is a no-op provided to simplify certain use cases. - - Anything else triggers `sqlite3_result_error()` with a - description of the problem. - - The first argument to this function is a `(sqlite3_context*)`. - Returns void. Does not throw. - */ - capi.sqlite3_result_js = function(pCtx, val) { - if (val instanceof Error) { - capi.sqlite3_result_error_js(pCtx, val); - return; - } - try { - switch (typeof val) { - case "undefined": break; - case "boolean": - capi.sqlite3_result_int(pCtx, val ? 1 : 0); - break; - case "bigint": - if (util.bigIntFits32(val)) capi.sqlite3_result_int(pCtx, Number(val)); - else if (util.bigIntFitsDouble(val)) capi.sqlite3_result_double(pCtx, Number(val)); - else if (wasm.bigIntEnabled) if (util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); - else toss3("BigInt value", val.toString(), "is too BigInt for int64."); - else toss3("BigInt value", val.toString(), "is too BigInt."); - break; - case "number": { - let f; - if (util.isInt32(val)) f = capi.sqlite3_result_int; - else if (wasm.bigIntEnabled && Number.isInteger(val) && util.bigIntFits64(BigInt(val))) f = capi.sqlite3_result_int64; - else f = capi.sqlite3_result_double; - f(pCtx, val); - break; - } - case "string": { - const [p, n] = wasm.allocCString(val, true); - capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC); - break; - } - case "object": if (null === val) { - capi.sqlite3_result_null(pCtx); - break; - } else if (util.isBindableTypedArray(val)) { - const pBlob = wasm.allocFromTypedArray(val); - capi.sqlite3_result_blob(pCtx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); - break; - } - default: toss3("Don't not how to handle this UDF result value:", typeof val, val); - } - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - /** - Returns the result sqlite3_column_value(pStmt,iCol) passed to - sqlite3_value_to_js(). The 3rd argument of this function is - ignored by this function except to pass it on as the second - argument of sqlite3_value_to_js(). If the sqlite3_column_value() - returns NULL (e.g. because the column index is out of range), - this function returns `undefined`, regardless of the 3rd - argument. If the 3rd argument is falsy and conversion fails, - `undefined` will be returned. - - Note that sqlite3_column_value() returns an "unprotected" value - object, but in a single-threaded environment (like this one) - there is no distinction between protected and unprotected values. - */ - capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert = true) { - const v = capi.sqlite3_column_value(pStmt, iCol); - return 0 === v ? void 0 : capi.sqlite3_value_to_js(v, throwIfCannotConvert); - }; - { - /** - Internal impl of sqlite3_preupdate_new/old_js() and - sqlite3changeset_new/old_js(). - */ - const __newOldValue = function(pObj, iCol, impl) { - impl = capi[impl]; - if (!this.ptr) this.ptr = wasm.allocPtr(); - else wasm.pokePtr(this.ptr, 0); - const rc = impl(pObj, iCol, this.ptr); - if (rc) return SQLite3Error.toss(rc, arguments[2] + "() failed with code " + rc); - const pv = wasm.peekPtr(this.ptr); - return pv ? capi.sqlite3_value_to_js(pv, true) : void 0; - }.bind(Object.create(null)); - /** - A wrapper around sqlite3_preupdate_new() which fetches the - sqlite3_value at the given index and returns the result of - passing it to sqlite3_value_to_js(). Throws on error. - */ - capi.sqlite3_preupdate_new_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_new"); - /** - The sqlite3_preupdate_old() counterpart of - sqlite3_preupdate_new_js(), with an identical interface. - */ - capi.sqlite3_preupdate_old_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_old"); - /** - A wrapper around sqlite3changeset_new() which fetches the - sqlite3_value at the given index and returns the result of - passing it to sqlite3_value_to_js(). Throws on error. - - If sqlite3changeset_new() succeeds but has no value to report, - this function returns the undefined value, noting that - undefined is not a valid conversion from an `sqlite3_value`, so - is unambiguous. - */ - capi.sqlite3changeset_new_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_new"); - /** - The sqlite3changeset_old() counterpart of - sqlite3changeset_new_js(), with an identical interface. - */ - capi.sqlite3changeset_old_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_old"); - } - const sqlite3 = { - WasmAllocError, - SQLite3Error, - capi, - util, - wasm, - config, - version: Object.create(null), - client: void 0, - asyncPostInit: async function ff() { - if (ff.isReady instanceof Promise) return ff.isReady; - let lia = this.initializersAsync; - delete this.initializersAsync; - const postInit = async () => { - if (!sqlite3.__isUnderTest) { - delete sqlite3.util; - delete sqlite3.StructBinder; - } - return sqlite3; - }; - const catcher = (e) => { - config.error("an async sqlite3 initializer failed:", e); - throw e; - }; - if (!lia || !lia.length) return ff.isReady = postInit().catch(catcher); - lia = lia.map((f) => { - return f instanceof Function ? async (x) => f(sqlite3) : f; - }); - lia.push(postInit); - let p = Promise.resolve(sqlite3); - while (lia.length) p = p.then(lia.shift()); - return ff.isReady = p.catch(catcher); - }.bind(sqlite3ApiBootstrap), - scriptInfo: void 0 - }; - if ("undefined" !== typeof sqlite3IsUnderTest) sqlite3.__isUnderTest = !!sqlite3IsUnderTest; - try { - sqlite3ApiBootstrap.initializers.forEach((f) => { - f(sqlite3); - }); - } catch (e) { - console.error("sqlite3 bootstrap initializer threw:", e); - throw e; - } - delete sqlite3ApiBootstrap.initializers; - sqlite3ApiBootstrap.sqlite3 = sqlite3; - if ("undefined" !== typeof sqlite3InitScriptInfo) { - sqlite3InitScriptInfo.debugModule("sqlite3ApiBootstrap() complete", sqlite3); - sqlite3.scriptInfo = sqlite3InitScriptInfo; - } - if (sqlite3.__isUnderTest) { - if ("undefined" !== typeof EmscriptenModule) sqlite3.config.emscripten = EmscriptenModule; - const iw = sqlite3.scriptInfo?.instantiateWasm; - if (iw) { - sqlite3.wasm.module = iw.module; - sqlite3.wasm.instance = iw.instance; - sqlite3.wasm.imports = iw.imports; - } - } - /** - Eliminate any confusion about whether these config objects may - be used after library initialization by eliminating the outward-facing - objects... - */ - delete globalThis.sqlite3ApiConfig; - delete globalThis.sqlite3ApiBootstrap; - delete sqlite3ApiBootstrap.defaultConfig; - return sqlite3.asyncPostInit().then((s) => { - if ("undefined" !== typeof sqlite3InitScriptInfo) sqlite3InitScriptInfo.debugModule("sqlite3.asyncPostInit() complete", s); - delete s.asyncPostInit; - delete s.scriptInfo; - delete s.emscripten; - return s; - }); - }; - /** - globalThis.sqlite3ApiBootstrap.initializers is an internal detail - used by the various pieces of the sqlite3 API's amalgamation - process. It must not be modified by client code except when plugging - such code into the amalgamation process. - - Each component of the amalgamation is expected to append a function - to this array. When sqlite3ApiBootstrap() is called for the first - time, each such function will be called (in their appended order) - and passed the sqlite3 namespace object, into which they can install - their features. At the end of that process, this array is deleted. - - The order of insertion into this array is significant for - some pieces. e.g. sqlite3.capi and sqlite3.wasm cannot be fully - utilized until the whwasmutil.js part is plugged in via - sqlite3-api-glue.js. - */ - globalThis.sqlite3ApiBootstrap.initializers = []; - /** - globalThis.sqlite3ApiBootstrap.initializersAsync is an internal detail - used by the sqlite3 API's amalgamation process. It must not be - modified by client code except when plugging such code into the - amalgamation process. - - The counterpart of globalThis.sqlite3ApiBootstrap.initializers, - specifically for initializers which are asynchronous. All entries in - this list must be either async functions, non-async functions which - return a Promise, or a Promise. Each function in the list is called - with the sqlite3 object as its only argument. - - The resolved value of any Promise is ignored and rejection will kill - the asyncPostInit() process (at an indeterminate point because all - of them are run asynchronously in parallel). - - This list is not processed until the client calls - sqlite3.asyncPostInit(). This means, for example, that intializers - added to globalThis.sqlite3ApiBootstrap.initializers may push entries to - this list. - */ - globalThis.sqlite3ApiBootstrap.initializersAsync = []; - /** - Client code may assign sqlite3ApiBootstrap.defaultConfig an - object-type value before calling sqlite3ApiBootstrap() (without - arguments) in order to tell that call to use this object as its - default config value. The intention of this is to provide - downstream clients with a reasonably flexible approach for plugging in - an environment-suitable configuration without having to define a new - global-scope symbol. - */ - globalThis.sqlite3ApiBootstrap.defaultConfig = Object.create(null); - /** - Placeholder: gets installed by the first call to - globalThis.sqlite3ApiBootstrap(). However, it is recommended that the - caller of sqlite3ApiBootstrap() capture its return value and delete - globalThis.sqlite3ApiBootstrap after calling it. It returns the same - value which will be stored here. - */ - globalThis.sqlite3ApiBootstrap.sqlite3 = void 0; - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - sqlite3.version = { - "libVersion": "3.52.0", - "libVersionNumber": 3052e3, - "sourceId": "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", - "downloadVersion": 352e4, - "scm": { - "sha3-256": "407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e", - "branch": "trunk", - "tags": "", - "datetime": "2026-01-30T06:37:34.096Z" - } - }; - }); - globalThis.WhWasmUtilInstaller = function WhWasmUtilInstaller(target) { - "use strict"; - if (void 0 === target.bigIntEnabled) target.bigIntEnabled = !!globalThis["BigInt64Array"]; - /** Throws a new Error, the message of which is the concatenation of - all args with a space between each. */ - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - if (!target.pointerSize && !target.pointerIR && target.alloc && target.dealloc) { - const ptr = target.alloc(1); - target.pointerSize = "bigint" === typeof ptr ? 8 : 4; - target.dealloc(ptr); - } - /** - As of 2025-09-21, this library works with 64-bit WASM modules - built with Emscripten's -sMEMORY64=1. - */ - if (target.pointerSize && !target.pointerIR) target.pointerIR = 4 === target.pointerSize ? "i32" : "i64"; - const __ptrIR = target.pointerIR ??= "i32"; - const __ptrSize = target.pointerSize ??= "i32" === __ptrIR ? 4 : "i64" === __ptrIR ? 8 : 0; - delete target.pointerSize; - delete target.pointerIR; - if ("i32" !== __ptrIR && "i64" !== __ptrIR) toss("Invalid pointerIR:", __ptrIR); - else if (8 !== __ptrSize && 4 !== __ptrSize) toss("Invalid pointerSize:", __ptrSize); - /** Either BigInt or, if !target.bigIntEnabled, a function which - throws complaining that BigInt is not enabled. */ - const __BigInt = target.bigIntEnabled ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); - const __Number = (v) => Number(v || 0); - /** - If target.ptr.ir==='i32' then this is equivalent to - Number(v||0) else it's equivalent to BigInt(v||0), throwing - if BigInt support is disabled. - - Why? Because Number(null)===0, but BigInt(null) throws. We - perform the same for Number to allow the undefined value to be - treated as a NULL WASM pointer, primarily to reduce friction in - many SQLite3 bindings which have long relied on that. - */ - const __asPtrType = 4 === __ptrSize ? __Number : __BigInt; - /** - The number 0 as either type Number or BigInt, depending on - target.ptr.ir. - */ - const __NullPtr = __asPtrType(0); - /** - Expects any number of numeric arguments, each one of either type - Number or BigInt. It sums them up (from an implicit starting - point of 0 or 0n) and returns them as a number of the same type - which target.ptr.coerce() uses. - - This is a workaround for not being able to mix Number/BigInt in - addition/subtraction expressions (which we frequently need for - calculating pointer offsets). - */ - const __ptrAdd = function(...args) { - let rc = __asPtrType(0); - for (const v of args) rc += __asPtrType(v); - return rc; - }; - /** Set up target.ptr... */ - { - const __ptr = Object.create(null); - Object.defineProperty(target, "ptr", { - enumerable: true, - get: () => __ptr, - set: () => toss("The ptr property is read-only.") - }); - (function f(name, val) { - Object.defineProperty(__ptr, name, { - enumerable: true, - get: () => val, - set: () => toss("ptr[" + name + "] is read-only.") - }); - return f; - })("null", __NullPtr)("size", __ptrSize)("ir", __ptrIR)("coerce", __asPtrType)("add", __ptrAdd)("addn", 4 === __ptrIR ? __ptrAdd : (...args) => Number(__ptrAdd(...args))); - } - if (!target.exports) Object.defineProperty(target, "exports", { - enumerable: true, - configurable: true, - get: () => target.instance?.exports - }); - /** Stores various cached state. */ - const cache = Object.create(null); - /** Previously-recorded size of cache.memory.buffer, noted so that - we can recreate the view objects if the heap grows. */ - cache.heapSize = 0; - /** WebAssembly.Memory object extracted from target.memory or - target.exports.memory the first time heapWrappers() is - called. */ - cache.memory = null; - /** uninstallFunction() puts table indexes in here for reuse and - installFunction() extracts them. */ - cache.freeFuncIndexes = []; - /** - List-of-lists used by scopedAlloc() and friends. - */ - cache.scopedAlloc = []; - /** Push the pointer ptr to the current cache.scopedAlloc list - (which must already exist) and return ptr. */ - cache.scopedAlloc.pushPtr = (ptr) => { - cache.scopedAlloc[cache.scopedAlloc.length - 1].push(ptr); - return ptr; - }; - cache.utf8Decoder = new TextDecoder(); - cache.utf8Encoder = new TextEncoder("utf-8"); - /** - For the given IR-like string in the set ('i8', 'i16', 'i32', - 'f32', 'float', 'i64', 'f64', 'double', '*'), or any string value - ending in '*', returns the sizeof for that value - (target.ptr.size in the latter case). For any other value, it - returns the undefined value. - */ - target.sizeofIR = (n) => { - switch (n) { - case "i8": return 1; - case "i16": return 2; - case "i32": - case "f32": - case "float": return 4; - case "i64": - case "f64": - case "double": return 8; - case "*": return __ptrSize; - default: return ("" + n).endsWith("*") ? __ptrSize : void 0; - } - }; - /** - If (cache.heapSize !== cache.memory.buffer.byteLength), i.e. if - the heap has grown since the last call, updates cache.HEAPxyz. - Returns the cache object. - */ - const heapWrappers = function() { - if (!cache.memory) cache.memory = target.memory instanceof WebAssembly.Memory ? target.memory : target.exports.memory; - else if (cache.heapSize === cache.memory.buffer.byteLength) return cache; - const b = cache.memory.buffer; - cache.HEAP8 = new Int8Array(b); - cache.HEAP8U = new Uint8Array(b); - cache.HEAP16 = new Int16Array(b); - cache.HEAP16U = new Uint16Array(b); - cache.HEAP32 = new Int32Array(b); - cache.HEAP32U = new Uint32Array(b); - cache.HEAP32F = new Float32Array(b); - cache.HEAP64F = new Float64Array(b); - if (target.bigIntEnabled) if ("undefined" !== typeof BigInt64Array) { - cache.HEAP64 = new BigInt64Array(b); - cache.HEAP64U = new BigUint64Array(b); - } else toss("BigInt support is enabled, but the BigInt64Array type is missing."); - cache.heapSize = b.byteLength; - return cache; - }; - /** Convenience equivalent of this.heapForSize(8,false). */ - target.heap8 = () => heapWrappers().HEAP8; - /** Convenience equivalent of this.heapForSize(8,true). */ - target.heap8u = () => heapWrappers().HEAP8U; - /** Convenience equivalent of this.heapForSize(16,false). */ - target.heap16 = () => heapWrappers().HEAP16; - /** Convenience equivalent of this.heapForSize(16,true). */ - target.heap16u = () => heapWrappers().HEAP16U; - /** Convenience equivalent of this.heapForSize(32,false). */ - target.heap32 = () => heapWrappers().HEAP32; - /** Convenience equivalent of this.heapForSize(32,true). */ - target.heap32u = () => heapWrappers().HEAP32U; - /** - Requires n to be one of: - - - integer 8, 16, or 32. - - A integer-type TypedArray constructor: Int8Array, Int16Array, - Int32Array, or their Uint counterparts. - - If this.bigIntEnabled is true, it also accepts the value 64 or a - BigInt64Array/BigUint64Array, else it throws if passed 64 or one - of those constructors. - - Returns an integer-based TypedArray view of the WASM heap memory - buffer associated with the given block size. If passed an integer - as the first argument and unsigned is truthy then the "U" - (unsigned) variant of that view is returned, else the signed - variant is returned. If passed a TypedArray value, the 2nd - argument is ignored. Float32Array and Float64Array views are not - supported by this function. - - Growth of the heap will invalidate any references to this heap, - so do not hold a reference longer than needed and do not use a - reference after any operation which may allocate. Instead, - re-fetch the reference by calling this function again. - - Throws if passed an invalid n. - */ - target.heapForSize = function(n, unsigned = true) { - const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); - switch (n) { - case Int8Array: return c.HEAP8; - case Uint8Array: return c.HEAP8U; - case Int16Array: return c.HEAP16; - case Uint16Array: return c.HEAP16U; - case Int32Array: return c.HEAP32; - case Uint32Array: return c.HEAP32U; - case 8: return unsigned ? c.HEAP8U : c.HEAP8; - case 16: return unsigned ? c.HEAP16U : c.HEAP16; - case 32: return unsigned ? c.HEAP32U : c.HEAP32; - case 64: - if (c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64; - break; - default: if (target.bigIntEnabled) { - if (n === globalThis["BigUint64Array"]) return c.HEAP64U; - else if (n === globalThis["BigInt64Array"]) return c.HEAP64; - break; - } - } - toss("Invalid heapForSize() size: expecting 8, 16, 32,", "or (if BigInt is enabled) 64."); - }; - const __funcTable = target.functionTable; - delete target.functionTable; - /** - Returns the WASM-exported "indirect function table". - */ - target.functionTable = __funcTable ? () => __funcTable : () => target.exports.__indirect_function_table; - /** - Given a function pointer, returns the WASM function table entry - if found, else returns a falsy value: undefined if fptr is out of - range or null if it's in range but the table entry is empty. - */ - target.functionEntry = function(fptr) { - const ft = target.functionTable(); - return fptr < ft.length ? ft.get(__asPtrType(fptr)) : void 0; - }; - /** - Creates a WASM function which wraps the given JS function and - returns the JS binding of that WASM function. The signature - string must be the Jaccwabyt-format or Emscripten - addFunction()-format function signature string. In short: in may - have one of the following formats: - - - Emscripten: `"x..."`, where the first x is a letter representing - the result type and subsequent letters represent the argument - types. Functions with no arguments have only a single - letter. - - - Jaccwabyt: `"x(...)"` where `x` is the letter representing the - result type and letters in the parens (if any) represent the - argument types. Functions with no arguments use `x()`. - - Supported letters: - - - `i` = int32 - - `p` = int32 or int64 ("pointer"), depending on target.ptr.size - - `j` = int64 - - `f` = float32 - - `d` = float64 - - `v` = void, only legal for use as the result type - - It throws if an invalid signature letter is used. - - Jaccwabyt-format signatures support some additional letters which - have no special meaning here but (in this context) act as aliases - for other letters: - - - `s`, `P`: same as `p` - - Sidebar: this code is developed together with Jaccwabyt, thus the - support for its signature format. - - The arguments may be supplied in either order: (func,sig) or - (sig,func). - */ - target.jsFuncToWasm = function f(func, sig) { - /** Attribution: adapted up from Emscripten-generated glue code, - refactored primarily for efficiency's sake, eliminating - call-local functions and superfluous temporary arrays. */ - if (!f._) f._ = { - sigTypes: Object.assign(Object.create(null), { - i: "i32", - p: __ptrIR, - P: __ptrIR, - s: __ptrIR, - j: "i64", - f: "f32", - d: "f64" - }), - typeCodes: Object.assign(Object.create(null), { - f64: 124, - f32: 125, - i64: 126, - i32: 127 - }), - uleb128Encode: (tgt, method, n) => { - if (n < 128) tgt[method](n); - else tgt[method](n % 128 | 128, n >> 7); - }, - rxJSig: /^(\w)\((\w*)\)$/, - sigParams: (sig) => { - const m = f._.rxJSig.exec(sig); - return m ? m[2] : sig.substr(1); - }, - letterType: (x) => f._.sigTypes[x] || toss("Invalid signature letter:", x), - pushSigType: (dest, letter) => dest.push(f._.typeCodes[f._.letterType(letter)]) - }; - if ("string" === typeof func) { - const x = sig; - sig = func; - func = x; - } - const _ = f._; - const sigParams = _.sigParams(sig); - const wasmCode = [1, 96]; - _.uleb128Encode(wasmCode, "push", sigParams.length); - for (const x of sigParams) _.pushSigType(wasmCode, x); - if ("v" === sig[0]) wasmCode.push(0); - else { - wasmCode.push(1); - _.pushSigType(wasmCode, sig[0]); - } - _.uleb128Encode(wasmCode, "unshift", wasmCode.length); - wasmCode.unshift(0, 97, 115, 109, 1, 0, 0, 0, 1); - wasmCode.push(2, 7, 1, 1, 101, 1, 102, 0, 0, 7, 5, 1, 1, 102, 0, 0); - return new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(wasmCode)), { e: { f: func } }).exports["f"]; - }; - /** - Documented as target.installFunction() except for the 3rd - argument: if truthy, the newly-created function pointer - is stashed in the current scoped-alloc scope and will be - cleaned up at the matching scopedAllocPop(), else it - is not stashed there. - */ - const __installFunction = function f(func, sig, scoped) { - if (scoped && !cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); - if ("string" === typeof func) { - const x = sig; - sig = func; - func = x; - } - if ("string" !== typeof sig || !(func instanceof Function)) toss("Invalid arguments: expecting (function,signature) or (signature,function)."); - const ft = target.functionTable(); - const oldLen = __asPtrType(ft.length); - let ptr; - while (ptr = cache.freeFuncIndexes.pop()) if (ft.get(ptr)) { - ptr = null; - continue; - } else break; - if (!ptr) { - ptr = __asPtrType(oldLen); - ft.grow(__asPtrType(1)); - } - try { - ft.set(ptr, func); - if (scoped) cache.scopedAlloc.pushPtr(ptr); - return ptr; - } catch (e) { - if (!(e instanceof TypeError)) { - if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); - throw e; - } - } - try { - const fptr = target.jsFuncToWasm(func, sig); - ft.set(ptr, fptr); - if (scoped) cache.scopedAlloc.pushPtr(ptr); - } catch (e) { - if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen); - throw e; - } - return ptr; - }; - /** - Expects a JS function and signature, exactly as for - this.jsFuncToWasm(). It uses that function to create a - WASM-exported function, installs that function to the next - available slot of this.functionTable(), and returns the - function's index in that table (which acts as a pointer to that - function). The returned pointer can be passed to - uninstallFunction() to uninstall it and free up the table slot - for reuse. - - If passed (string,function) arguments then it treats the first - argument as the signature and second as the function. - - As a special case, if the passed-in function is a WASM-exported - function then the signature argument is ignored and func is - installed as-is, without requiring re-compilation/re-wrapping. - - This function will propagate an exception if - WebAssembly.Table.grow() throws or this.jsFuncToWasm() throws. - The former case can happen in an Emscripten-compiled environment - when building without Emscripten's `-sALLOW_TABLE_GROWTH` flag. - - Sidebar: this function differs from Emscripten's addFunction() - _primarily_ in that it does not share that function's - undocumented behavior of reusing a function if it's passed to - addFunction() more than once, which leads to uninstallFunction() - breaking clients which do not take care to avoid that case: - - https://github.com/emscripten-core/emscripten/issues/17323 - */ - target.installFunction = (func, sig) => __installFunction(func, sig, false); - /** - Works exactly like installFunction() but requires that a - scopedAllocPush() is active and uninstalls the given function - when that alloc scope is popped via scopedAllocPop(). - This is used for implementing JS/WASM function bindings which - should only persist for the life of a call into a single - C-side function. - */ - target.scopedInstallFunction = (func, sig) => __installFunction(func, sig, true); - /** - Requires a pointer value previously returned from - this.installFunction(). Removes that function from the WASM - function table, marks its table slot as free for re-use, and - returns that function. It is illegal to call this before - installFunction() has been called and results are undefined if - ptr was not returned by that function. The returned function - may be passed back to installFunction() to reinstall it. - - To simplify certain use cases, if passed a falsy non-0 value - (noting that 0 is a valid function table index), this function - has no side effects and returns undefined. - */ - target.uninstallFunction = function(ptr) { - if (!ptr && __NullPtr !== ptr) return void 0; - const ft = target.functionTable(); - cache.freeFuncIndexes.push(ptr); - const rc = ft.get(ptr); - ft.set(ptr, null); - return rc; - }; - /** - Given a WASM heap memory address and a data type name in the form - (i8, i16, i32, i64, float (or f32), double (or f64)), this - fetches the numeric value from that address and returns it as a - number or, for the case of type='i64', a BigInt (with the caveat - BigInt will trigger an exception if this.bigIntEnabled is - falsy). Throws if given an invalid type. - - If the first argument is an array, it is treated as an array of - addresses and the result is an array of the values from each of - those address, using the same 2nd argument for determining the - value type to fetch. - - As a special case, if type ends with a `*`, it is considered to - be a pointer type and is treated as the WASM numeric type - appropriate for the pointer size (==this.ptr.ir). - - While possibly not obvious, this routine and its poke() - counterpart are how pointer-to-value _output_ parameters in - WASM-compiled C code can be interacted with: - - ``` - const ptr = alloc(4); - poke32(ptr, 0); // clear the ptr's value - aCFuncWithOutputPtrToInt32Arg(ptr); // e.g. void foo(int *x); - const result = peek32(ptr); // fetch ptr's value - dealloc(ptr); - ``` - - scopedAlloc() and friends can be used to make handling of - `ptr` safe against leaks in the case of an exception: - - ``` - let result; - const scope = scopedAllocPush(); - try{ - const ptr = scopedAlloc(4); - poke32(ptr, 0); - aCFuncWithOutputPtrArg(ptr); - result = peek32(ptr); - }finally{ - scopedAllocPop(scope); - } - ``` - - As a rule poke() must be called to set (typically zero out) the - pointer's value, else it will contain an essentially random - value. - - ACHTUNG: calling this often, e.g. in a loop, can have a noticably - painful impact on performance. Rather than doing so, use - heapForSize() to fetch the heap object and read directly from it. - - ACHTUNG #2: ptr may be a BigInt (and generally will be in 64-bit - builds) but this function must coerce it into a Number in order - to access the heap's contents. Ergo: BitInts outside of the - (extrardinarily genereous) address range exposed to browser-side - WASM may cause misbehavior. - - See also: poke() - */ - target.peek = function f(ptr, type = "i8") { - if (type.endsWith("*")) type = __ptrIR; - const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); - const list = Array.isArray(ptr) ? [] : void 0; - let rc; - do { - if (list) ptr = arguments[0].shift(); - switch (type) { - case "i1": - case "i8": - rc = c.HEAP8[Number(ptr) >> 0]; - break; - case "i16": - rc = c.HEAP16[Number(ptr) >> 1]; - break; - case "i32": - rc = c.HEAP32[Number(ptr) >> 2]; - break; - case "float": - case "f32": - rc = c.HEAP32F[Number(ptr) >> 2]; - break; - case "double": - case "f64": - rc = Number(c.HEAP64F[Number(ptr) >> 3]); - break; - case "i64": if (c.HEAP64) { - rc = __BigInt(c.HEAP64[Number(ptr) >> 3]); - break; - } - default: toss("Invalid type for peek():", type); - } - if (list) list.push(rc); - } while (list && arguments[0].length); - return list || rc; - }; - /** - The counterpart of peek(), this sets a numeric value at the given - WASM heap address, using the 3rd argument to define how many - bytes are written. Throws if given an invalid type. See peek() - for details about the `type` argument. If the 3rd argument ends - with `*` then it is treated as a pointer type and this function - behaves as if the 3rd argument were this.ptr.ir. - - If the first argument is an array, it is treated like a list - of pointers and the given value is written to each one. - - Returns `this`. (Prior to 2022-12-09 it returned this function.) - - ACHTUNG #1: see peek()'s ACHTUNG #1. - - ACHTUNG #2: see peek()'s ACHTUNG #2. - */ - target.poke = function(ptr, value, type = "i8") { - if (type.endsWith("*")) type = __ptrIR; - const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers(); - for (const p of Array.isArray(ptr) ? ptr : [ptr]) switch (type) { - case "i1": - case "i8": - c.HEAP8[Number(p) >> 0] = value; - continue; - case "i16": - c.HEAP16[Number(p) >> 1] = value; - continue; - case "i32": - c.HEAP32[Number(p) >> 2] = value; - continue; - case "float": - case "f32": - c.HEAP32F[Number(p) >> 2] = value; - continue; - case "double": - case "f64": - c.HEAP64F[Number(p) >> 3] = value; - continue; - case "i64": if (c.HEAP64) { - c.HEAP64[Number(p) >> 3] = __BigInt(value); - continue; - } - default: toss("Invalid type for poke(): " + type); - } - return this; - }; - /** - Convenience form of peek() intended for fetching - pointer-to-pointer values. If passed a single non-array argument - it returns the value of that one pointer address. If passed - multiple arguments, or a single array of arguments, it returns an - array of their values. - */ - target.peekPtr = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, __ptrIR); - /** - A variant of poke() intended for setting pointer-to-pointer - values. Its differences from poke() are that (1) it defaults to a - value of 0 and (2) it always writes to the pointer-sized heap - view. - */ - target.pokePtr = (ptr, value = 0) => target.poke(ptr, value, __ptrIR); - /** - Convenience form of peek() intended for fetching i8 values. If - passed a single non-array argument it returns the value of that - one pointer address. If passed multiple arguments, or a single - array of arguments, it returns an array of their values. - */ - target.peek8 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i8"); - /** - Convenience form of poke() intended for setting individual bytes. - Its difference from poke() is that it always writes to the - i8-sized heap view. - */ - target.poke8 = (ptr, value) => target.poke(ptr, value, "i8"); - /** i16 variant of peek8(). */ - target.peek16 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i16"); - /** i16 variant of poke8(). */ - target.poke16 = (ptr, value) => target.poke(ptr, value, "i16"); - /** i32 variant of peek8(). */ - target.peek32 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i32"); - /** i32 variant of poke8(). */ - target.poke32 = (ptr, value) => target.poke(ptr, value, "i32"); - /** i64 variant of peek8(). Will throw if this build is not - configured for BigInt support. */ - target.peek64 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i64"); - /** i64 variant of poke8(). Will throw if this build is not - configured for BigInt support. Note that this returns - a BigInt-type value, not a Number-type value. */ - target.poke64 = (ptr, value) => target.poke(ptr, value, "i64"); - /** f32 variant of peek8(). */ - target.peek32f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f32"); - /** f32 variant of poke8(). */ - target.poke32f = (ptr, value) => target.poke(ptr, value, "f32"); - /** f64 variant of peek8(). */ - target.peek64f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f64"); - /** f64 variant of poke8(). */ - target.poke64f = (ptr, value) => target.poke(ptr, value, "f64"); - /** Deprecated alias for getMemValue() */ - target.getMemValue = target.peek; - /** Deprecated alias for peekPtr() */ - target.getPtrValue = target.peekPtr; - /** Deprecated alias for poke() */ - target.setMemValue = target.poke; - /** Deprecated alias for pokePtr() */ - target.setPtrValue = target.pokePtr; - /** - Returns true if the given value appears to be legal for use as - a WASM pointer value. Its _range_ of values is not (cannot be) - validated except to ensure that it is a 32-bit integer with a - value of 0 or greater. Likewise, it cannot verify whether the - value actually refers to allocated memory in the WASM heap. - - Whether or not null or undefined are legal are context-dependent. - They generally are legal but this function does not treat them as - such because they're not strictly legal for passing as-is as WASM - integer arguments. - */ - target.isPtr32 = (ptr) => "number" === typeof ptr && ptr >= 0 && ptr === (ptr | 0); - /** 64-bit counterpart of isPtr32() and falls back to that function - if ptr is not a BigInt. */ - target.isPtr64 = (ptr) => "bigint" === typeof ptr ? ptr >= 0 : target.isPtr32(ptr); - /** - isPtr() is an alias for isPtr32() or isPtr64(), depending on the - value of target.ptr.size. - */ - target.isPtr = 4 === __ptrSize ? target.isPtr32 : target.isPtr64; - /** - Expects ptr to be a pointer into the WASM heap memory which - refers to a NUL-terminated C-style string encoded as UTF-8. - Returns the length, in bytes, of the string, as for `strlen(3)`. - As a special case, if !ptr or if it's not a pointer then it - returns `null`. Throws if ptr is out of range for - target.heap8u(). - */ - target.cstrlen = function(ptr) { - if (!ptr || !target.isPtr(ptr)) return null; - ptr = Number(ptr); - const h = heapWrappers().HEAP8U; - let pos = ptr; - for (; h[pos] !== 0; ++pos); - return pos - ptr; - }; - /** Internal helper to use in operations which need to distinguish - between TypedArrays which are backed by a SharedArrayBuffer - from those which are not. */ - const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; - /** Returns true if the given TypedArray object is backed by a - SharedArrayBuffer, else false. */ - const isSharedTypedArray = (aTypedArray) => aTypedArray.buffer instanceof __SAB; - target.isSharedTypedArray = isSharedTypedArray; - /** - Returns either aTypedArray.slice(begin,end) (if - aTypedArray.buffer is a SharedArrayBuffer) or - aTypedArray.subarray(begin,end) (if it's not). - - This distinction is important for APIs which don't like to - work on SABs, e.g. TextDecoder, and possibly for our - own APIs which work on memory ranges which "might" be - modified by other threads while they're working. - - begin and end may be of type Number or (in 64-bit builds) BigInt - (which get coerced to Numbers). - */ - const typedArrayPart = (aTypedArray, begin, end) => { - if (8 === __ptrSize) { - if ("bigint" === typeof begin) begin = Number(begin); - if ("bigint" === typeof end) end = Number(end); - } - return isSharedTypedArray(aTypedArray) ? aTypedArray.slice(begin, end) : aTypedArray.subarray(begin, end); - }; - target.typedArrayPart = typedArrayPart; - /** - Uses TextDecoder to decode the given half-open range of the given - TypedArray to a string. This differs from a simple call to - TextDecoder in that it accounts for whether the first argument is - backed by a SharedArrayBuffer or not, and can work more - efficiently if it's not (TextDecoder refuses to act upon an SAB). - - If begin/end are not provided or are falsy then each defaults to - the start/end of the array. - */ - const typedArrayToString = (typedArray, begin, end) => cache.utf8Decoder.decode(typedArrayPart(typedArray, begin, end)); - target.typedArrayToString = typedArrayToString; - /** - Expects ptr to be a pointer into the WASM heap memory which - refers to a NUL-terminated C-style string encoded as UTF-8. This - function counts its byte length using cstrlen() then returns a - JS-format string representing its contents. As a special case, if - ptr is falsy or not a pointer, `null` is returned. - */ - target.cstrToJs = function(ptr) { - const n = target.cstrlen(ptr); - return n ? typedArrayToString(heapWrappers().HEAP8U, Number(ptr), Number(ptr) + n) : null === n ? n : ""; - }; - /** - Given a JS string, this function returns its UTF-8 length in - bytes. Returns null if str is not a string. - */ - target.jstrlen = function(str) { - /** Attribution: derived from Emscripten's lengthBytesUTF8() */ - if ("string" !== typeof str) return null; - const n = str.length; - let len = 0; - for (let i = 0; i < n; ++i) { - let u = str.charCodeAt(i); - if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023; - if (u <= 127) ++len; - else if (u <= 2047) len += 2; - else if (u <= 65535) len += 3; - else len += 4; - } - return len; - }; - /** - Encodes the given JS string as UTF8 into the given TypedArray - tgt, starting at the given offset and writing, at most, maxBytes - bytes (including the NUL terminator if addNul is true, else no - NUL is added). If it writes any bytes at all and addNul is true, - it always NUL-terminates the output, even if doing so means that - the NUL byte is all that it writes. - - If maxBytes is negative (the default) then it is treated as the - remaining length of tgt, starting at the given offset. - - If writing the last character would surpass the maxBytes count - because the character is multi-byte, that character will not be - written (as opposed to writing a truncated multi-byte character). - This can lead to it writing as many as 3 fewer bytes than - maxBytes specifies. - - Returns the number of bytes written to the target, _including_ - the NUL terminator (if any). If it returns 0, it wrote nothing at - all, which can happen if: - - - str is empty and addNul is false. - - offset < 0. - - maxBytes == 0. - - maxBytes is less than the byte length of a multi-byte str[0]. - - Throws if tgt is not an Int8Array or Uint8Array. - - Design notes: - - - In C's strcpy(), the destination pointer is the first - argument. That is not the case here primarily because the 3rd+ - arguments are all referring to the destination, so it seems to - make sense to have them grouped with it. - - - Emscripten's counterpart of this function (stringToUTF8Array()) - returns the number of bytes written sans NUL terminator. That - is, however, ambiguous: str.length===0 or maxBytes===(0 or 1) - all cause 0 to be returned. - */ - target.jstrcpy = function(jstr, tgt, offset = 0, maxBytes = -1, addNul = true) { - /** Attribution: the encoding bits are taken from Emscripten's - stringToUTF8Array(). */ - if (!tgt || !(tgt instanceof Int8Array) && !(tgt instanceof Uint8Array)) toss("jstrcpy() target must be an Int8Array or Uint8Array."); - maxBytes = Number(maxBytes); - offset = Number(offset); - if (maxBytes < 0) maxBytes = tgt.length - offset; - if (!(maxBytes > 0) || !(offset >= 0)) return 0; - let i = 0, max = jstr.length; - const begin = offset, end = offset + maxBytes - (addNul ? 1 : 0); - for (; i < max && offset < end; ++i) { - let u = jstr.charCodeAt(i); - if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | jstr.charCodeAt(++i) & 1023; - if (u <= 127) { - if (offset >= end) break; - tgt[offset++] = u; - } else if (u <= 2047) { - if (offset + 1 >= end) break; - tgt[offset++] = 192 | u >> 6; - tgt[offset++] = 128 | u & 63; - } else if (u <= 65535) { - if (offset + 2 >= end) break; - tgt[offset++] = 224 | u >> 12; - tgt[offset++] = 128 | u >> 6 & 63; - tgt[offset++] = 128 | u & 63; - } else { - if (offset + 3 >= end) break; - tgt[offset++] = 240 | u >> 18; - tgt[offset++] = 128 | u >> 12 & 63; - tgt[offset++] = 128 | u >> 6 & 63; - tgt[offset++] = 128 | u & 63; - } - } - if (addNul) tgt[offset++] = 0; - return offset - begin; - }; - /** - Works similarly to C's strncpy(), copying, at most, n bytes (not - characters) from srcPtr to tgtPtr. It copies until n bytes have - been copied or a 0 byte is reached in src. _Unlike_ strncpy(), it - returns the number of bytes it assigns in tgtPtr, _including_ the - NUL byte (if any). If n is reached before a NUL byte in srcPtr, - tgtPtr will _not_ be NULL-terminated. If a NUL byte is reached - before n bytes are copied, tgtPtr will be NUL-terminated. - - If n is negative, cstrlen(srcPtr)+1 is used to calculate it, the - +1 being for the NUL byte. - - Throws if tgtPtr or srcPtr are falsy. Results are undefined if: - - - either is not a pointer into the WASM heap or - - - srcPtr is not NUL-terminated AND n is less than srcPtr's - logical length. - - ACHTUNG: it is possible to copy partial multi-byte characters - this way, and converting such strings back to JS strings will - have undefined results. - */ - target.cstrncpy = function(tgtPtr, srcPtr, n) { - if (!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings."); - if (n < 0) n = target.cstrlen(strPtr) + 1; - else if (!(n > 0)) return 0; - const heap = target.heap8u(); - let i = 0, ch; - const tgtNumber = Number(tgtPtr), srcNumber = Number(srcPtr); - for (; i < n && (ch = heap[srcNumber + i]); ++i) heap[tgtNumber + i] = ch; - if (i < n) heap[tgtNumber + i++] = 0; - return i; - }; - /** - For the given JS string, returns a Uint8Array of its contents - encoded as UTF-8. If addNul is true, the returned array will have - a trailing 0 entry, else it will not. - */ - target.jstrToUintArray = (str, addNul = false) => { - return cache.utf8Encoder.encode(addNul ? str + "\0" : str); - }; - const __affirmAlloc = (obj, funcName) => { - if (!(obj.alloc instanceof Function) || !(obj.dealloc instanceof Function)) toss("Object is missing alloc() and/or dealloc() function(s)", "required by", funcName + "()."); - }; - const __allocCStr = function(jstr, returnWithLength, allocator, funcName) { - __affirmAlloc(target, funcName); - if ("string" !== typeof jstr) return null; - const u = cache.utf8Encoder.encode(jstr), ptr = allocator(u.length + 1); - let toFree = ptr; - try { - const heap = heapWrappers().HEAP8U; - heap.set(u, Number(ptr)); - heap[__ptrAdd(ptr, u.length)] = 0; - toFree = __NullPtr; - return returnWithLength ? [ptr, u.length] : ptr; - } finally { - if (toFree) target.dealloc(toFree); - } - }; - /** - Uses target.alloc() to allocate enough memory for jstrlen(jstr)+1 - bytes of memory, copies jstr to that memory using jstrcpy(), - NUL-terminates it, and returns the pointer to that C-string. - Ownership of the pointer is transfered to the caller, who must - eventually pass the pointer to dealloc() to free it. - - If passed a truthy 2nd argument then its return semantics change: - it returns [ptr,n], where ptr is the C-string's pointer and n is - its cstrlen(). - - Throws if `target.alloc` or `target.dealloc` are not functions. - */ - target.allocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.alloc, "allocCString()"); - /** - Starts an "allocation scope." All allocations made using - scopedAlloc() are recorded in this scope and are freed when the - value returned from this function is passed to - scopedAllocPop(). - - This family of functions requires that the API's object have both - `alloc()` and `dealloc()` methods, else this function will throw. - - Intended usage: - - ``` - const scope = scopedAllocPush(); - try { - const ptr1 = scopedAlloc(100); - const ptr2 = scopedAlloc(200); - const ptr3 = scopedAlloc(300); - ... - // Note that only allocations made via scopedAlloc() - // are managed by this allocation scope. - }finally{ - scopedAllocPop(scope); - } - ``` - - The value returned by this function must be treated as opaque by - the caller, suitable _only_ for passing to scopedAllocPop(). - Its type and value are not part of this function's API and may - change in any given version of this code. - - `scopedAlloc.level` can be used to determine how many scoped - alloc levels are currently active. - */ - target.scopedAllocPush = function() { - __affirmAlloc(target, "scopedAllocPush"); - const a = []; - cache.scopedAlloc.push(a); - return a; - }; - /** - Cleans up all allocations made using scopedAlloc() in the context - of the given opaque state object, which must be a value returned - by scopedAllocPush(). See that function for an example of how to - use this function. It also uninstalls any WASM functions - installed with scopedInstallFunction(). - - Though scoped allocations are managed like a stack, this API - behaves properly if allocation scopes are popped in an order - other than the order they were pushed. The intent is that it - _always_ be used in a stack-like manner. - - If called with no arguments, it pops the most recent - scopedAllocPush() result: - - ``` - scopedAllocPush(); - try{ ... } finally { scopedAllocPop(); } - ``` - - It's generally recommended that it be passed an explicit argument - to help ensure that push/push are used in matching pairs, but in - trivial code that may be a non-issue. - */ - target.scopedAllocPop = function(state) { - __affirmAlloc(target, "scopedAllocPop"); - const n = arguments.length ? cache.scopedAlloc.indexOf(state) : cache.scopedAlloc.length - 1; - if (n < 0) toss("Invalid state object for scopedAllocPop()."); - if (0 === arguments.length) state = cache.scopedAlloc[n]; - cache.scopedAlloc.splice(n, 1); - for (let p; p = state.pop();) if (target.functionEntry(p)) target.uninstallFunction(p); - else target.dealloc(p); - }; - /** - Allocates n bytes of memory using this.alloc() and records that - fact in the state for the most recent call of scopedAllocPush(). - Ownership of the memory is given to scopedAllocPop(), which - will clean it up when it is called. The memory _must not_ be - passed to this.dealloc(). Throws if this API object is missing - the required `alloc()` or `dealloc()` functions or no scoped - alloc is active. - - See scopedAllocPush() for an example of how to use this function. - - The `level` property of this function can be queried to query how - many scoped allocation levels are currently active. - - See also: scopedAllocPtr(), scopedAllocCString() - */ - target.scopedAlloc = function(n) { - if (!cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active."); - const p = __asPtrType(target.alloc(n)); - return cache.scopedAlloc.pushPtr(p); - }; - Object.defineProperty(target.scopedAlloc, "level", { - configurable: false, - enumerable: false, - get: () => cache.scopedAlloc.length, - set: () => toss("The 'active' property is read-only.") - }); - /** - Works identically to allocCString() except that it allocates the - memory using scopedAlloc(). - - Will throw if no scopedAllocPush() call is active. - */ - target.scopedAllocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.scopedAlloc, "scopedAllocCString()"); - const __allocMainArgv = function(isScoped, list) { - const pList = target[isScoped ? "scopedAlloc" : "alloc"]((list.length + 1) * target.ptr.size); - let i = 0; - list.forEach((e) => { - target.pokePtr(__ptrAdd(pList, target.ptr.size * i++), target[isScoped ? "scopedAllocCString" : "allocCString"]("" + e)); - }); - target.pokePtr(__ptrAdd(pList, target.ptr.size * i), 0); - return pList; - }; - /** - Creates an array, using scopedAlloc(), suitable for passing to a - C-level main() routine. The input is a collection with a length - property and a forEach() method. A block of memory - (list.length+1) entries long is allocated and each pointer-sized - block of that memory is populated with a scopedAllocCString() - conversion of the (""+value) of each element, with the exception - that the final entry is a NULL pointer. Returns a pointer to the - start of the list, suitable for passing as the 2nd argument to a - C-style main() function. - - Throws if scopedAllocPush() is not active. - - Design note: the returned array is allocated with an extra NULL - pointer entry to accommodate certain APIs, but client code which - does not need that functionality should treat the returned array - as list.length entries long. - */ - target.scopedAllocMainArgv = (list) => __allocMainArgv(true, list); - /** - Identical to scopedAllocMainArgv() but uses alloc() instead of - scopedAlloc(). - */ - target.allocMainArgv = (list) => __allocMainArgv(false, list); - /** - Expects to be given a C-style string array and its length. It - returns a JS array of strings and/or nulls: any entry in the - pArgv array which is NULL results in a null entry in the result - array. If argc is 0 then an empty array is returned. - - Results are undefined if any entry in the first argc entries of - pArgv are neither 0 (NULL) nor legal UTF-format C strings. - - To be clear, the expected C-style arguments to be passed to this - function are `(int, char **)` (optionally const-qualified). - */ - target.cArgvToJs = (argc, pArgv) => { - const list = []; - for (let i = 0; i < argc; ++i) { - const arg = target.peekPtr(__ptrAdd(pArgv, target.ptr.size * i)); - list.push(arg ? target.cstrToJs(arg) : null); - } - return list; - }; - /** - Wraps function call func() in a scopedAllocPush() and - scopedAllocPop() block, such that all calls to scopedAlloc() and - friends from within that call will have their memory freed - automatically when func() returns. If func throws or propagates - an exception, the scope is still popped, otherwise it returns the - result of calling func(). - */ - target.scopedAllocCall = function(func) { - target.scopedAllocPush(); - try { - return func(); - } finally { - target.scopedAllocPop(); - } - }; - /** Internal impl for allocPtr() and scopedAllocPtr(). */ - const __allocPtr = function(howMany, safePtrSize, method) { - __affirmAlloc(target, method); - const pIr = safePtrSize ? "i64" : __ptrIR; - let m = target[method](howMany * (safePtrSize ? 8 : __ptrSize)); - target.poke(m, 0, pIr); - if (1 === howMany) return m; - const a = [m]; - for (let i = 1; i < howMany; ++i) { - m = __ptrAdd(m, safePtrSize ? 8 : __ptrSize); - a[i] = m; - target.poke(m, 0, pIr); - } - return a; - }; - /** - Allocates one or more pointers as a single chunk of memory and - zeroes them out. - - The first argument is the number of pointers to allocate. The - second specifies whether they should use a "safe" pointer size (8 - bytes) or whether they may use the default pointer size - (typically 4 but also possibly 8). - - How the result is returned depends on its first argument: if - passed 1, it returns the allocated memory address. If passed more - than one then an array of pointer addresses is returned, which - can optionally be used with "destructuring assignment" like this: - - ``` - const [p1, p2, p3] = allocPtr(3); - ``` - - ACHTUNG: when freeing the memory, pass only the _first_ result - value to dealloc(). The others are part of the same memory chunk - and must not be freed separately. - - The reason for the 2nd argument is... - - When one of the returned pointers will refer to a 64-bit value, - e.g. a double or int64, and that value must be written or fetched, - e.g. using poke() or peek(), it is important that - the pointer in question be aligned to an 8-byte boundary or else - it will not be fetched or written properly and will corrupt or - read neighboring memory. It is only safe to pass false when the - client code is certain that it will only get/fetch 4-byte values - (or smaller). - */ - target.allocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "alloc"); - /** - Identical to allocPtr() except that it allocates using scopedAlloc() - instead of alloc(). - */ - target.scopedAllocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "scopedAlloc"); - /** - If target.exports[name] exists, it is returned, else an - exception is thrown. - */ - target.xGet = function(name) { - return target.exports[name] || toss("Cannot find exported symbol:", name); - }; - const __argcMismatch = (f, n) => toss(f + "() requires", n, "argument(s)."); - /** - Looks up a WASM-exported function named fname from - target.exports. If found, it is called, passed all remaining - arguments, and its return value is returned to xCall's caller. If - not found, an exception is thrown. This function does no - conversion of argument or return types, but see xWrap() and - xCallWrapped() for variants which do. - - If the first argument is a function is is assumed to be - a WASM-bound function and is used as-is instead of looking up - the function via xGet(). - - As a special case, if passed only 1 argument after the name and - that argument in an Array, that array's entries become the - function arguments. (This is not an ambiguous case because it's - not legal to pass an Array object to a WASM function.) - */ - target.xCall = function(fname, ...args) { - const f = fname instanceof Function ? fname : target.xGet(fname); - if (!(f instanceof Function)) toss("Exported symbol", fname, "is not a function."); - if (f.length !== args.length) __argcMismatch(f === fname ? f.name : fname, f.length); - return 2 === arguments.length && Array.isArray(arguments[1]) ? f.apply(null, arguments[1]) : f.apply(null, args); - }; - /** - State for use with xWrap(). - */ - cache.xWrap = Object.create(null); - cache.xWrap.convert = Object.create(null); - /** Map of type names to argument conversion functions. */ - cache.xWrap.convert.arg = /* @__PURE__ */ new Map(); - /** Map of type names to return result conversion functions. */ - cache.xWrap.convert.result = /* @__PURE__ */ new Map(); - /** Scope-local convenience aliases. */ - const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result; - const __xArgPtr = __asPtrType; - xArg.set("i64", __BigInt).set("i32", (i) => i | 0).set("i16", (i) => (i | 0) & 65535).set("i8", (i) => (i | 0) & 255).set("f32", (i) => Number(i).valueOf()).set("float", xArg.get("f32")).set("f64", xArg.get("f32")).set("double", xArg.get("f64")).set("int", xArg.get("i32")).set("null", (i) => i).set(null, xArg.get("null")).set("**", __xArgPtr).set("*", __xArgPtr); - xResult.set("*", __xArgPtr).set("pointer", __xArgPtr).set("number", (v) => Number(v)).set("void", (v) => void 0).set(void 0, xResult.get("void")).set("null", (v) => v).set(null, xResult.get("null")); - for (const t of [ - "i8", - "i16", - "i32", - "i64", - "int", - "f32", - "float", - "f64", - "double" - ]) { - xArg.set(t + "*", __xArgPtr); - xResult.set(t + "*", __xArgPtr); - xResult.set(t, xArg.get(t) || toss("Maintenance required: missing arg converter for", t)); - } - /** - In order for args of type string to work in various contexts in - the sqlite3 API, we need to pass them on as, variably, a C-string - or a pointer value. Thus for ARGs of type 'string' and - '*'/'pointer' we behave differently depending on whether the - argument is a string or not: - - - If v is a string, scopeAlloc() a new C-string from it and return - that temp string's pointer. - - - Else return the value from the arg adapter defined for - target.ptr.ir. - - TODO? Permit an Int8Array/Uint8Array and convert it to a string? - Would that be too much magic concentrated in one place, ready to - backfire? We handle that at the client level in sqlite3 with a - custom argument converter. - */ - const __xArgString = (v) => { - return "string" === typeof v ? target.scopedAllocCString(v) : __asPtrType(v); - }; - xArg.set("string", __xArgString).set("utf8", __xArgString); - xResult.set("string", (i) => target.cstrToJs(i)).set("utf8", xResult.get("string")).set("string:dealloc", (i) => { - try { - return i ? target.cstrToJs(i) : null; - } finally { - target.dealloc(i); - } - }).set("utf8:dealloc", xResult.get("string:dealloc")).set("json", (i) => JSON.parse(target.cstrToJs(i))).set("json:dealloc", (i) => { - try { - return i ? JSON.parse(target.cstrToJs(i)) : null; - } finally { - target.dealloc(i); - } - }); - /** - Internal-use-only base class for FuncPtrAdapter and potentially - additional stateful argument adapter classes. - - Its main interface (convertArg()) is strictly internal, not to be - exposed to client code, as it may still need re-shaping. Only the - constructors of concrete subclasses should be exposed to clients, - and those in such a way that does not hinder internal redesign of - the convertArg() interface. - */ - const AbstractArgAdapter = class { - constructor(opt) { - this.name = opt.name || "unnamed adapter"; - } - /** - Gets called via xWrap() to "convert" v to whatever type - this specific class supports. - - argIndex is the argv index of _this_ argument in the - being-xWrap()'d call. argv is the current argument list - undergoing xWrap() argument conversion. argv entries to the - left of argIndex will have already undergone transformation and - those to the right will not have (they will have the values the - client-level code passed in, awaiting conversion). The RHS - indexes must never be relied upon for anything because their - types are indeterminate, whereas the LHS values will be - WASM-compatible values by the time this is called. - - The reason for the argv and argIndex arguments is that we - frequently need more context than v for a specific conversion, - and that context invariably lies in the LHS arguments of v. - Examples of how this is useful can be found in FuncPtrAdapter. - */ - convertArg(v, argv, argIndex) { - toss("AbstractArgAdapter must be subclassed."); - } - }; - /** - This type is recognized by xWrap() as a proxy for converting a JS - function to a C-side function, either permanently, for the - duration of a single call into the C layer, or semi-contextual, - where it may keep track of a single binding for a given context - and uninstall the binding if it's replaced. - - The constructor requires an options object with these properties: - - - name (optional): string describing the function binding. This - is solely for debugging and error-reporting purposes. If not - provided, an empty string is assumed. - - - signature: a function signature string compatible with - jsFuncToWasm(). - - - bindScope (string): one of ('transient', 'context', - 'singleton', 'permanent'). Bind scopes are: - - - 'transient': it will convert JS functions to WASM only for - the duration of the xWrap()'d function call, using - scopedInstallFunction(). Before that call returns, the - WASM-side binding will be uninstalled. - - - 'singleton': holds one function-pointer binding for this - instance. If it's called with a different function pointer, - it uninstalls the previous one after converting the new - value. This is only useful for use with "global" functions - which do not rely on any state other than this function - pointer. If the being-converted function pointer is intended - to be mapped to some sort of state object (e.g. an - `sqlite3*`) then "context" (see below) is the proper mode. - - - 'context': similar to singleton mode but for a given - "context", where the context is a key provided by the user - and possibly dependent on a small amount of call-time - context. This mode is the default if bindScope is _not_ set - but a property named contextKey (described below) is. - - - 'permanent': the function is installed and left there - forever. There is no way to recover its pointer address - later on for cleanup purposes. i.e. it effectively leaks. - - - callProxy (function): if set, this must be a function which - will act as a proxy for any "converted" JS function. It is - passed the being-converted function value and must return - either that function or a function which acts on its - behalf. The returned function will be the one which gets - installed into the WASM function table. The proxy must perform - any required argument conversion (it will be called from C - code, so will receive C-format arguments) before passing them - on to the being-converted function. Whether or not the proxy - itself must return a value depends on the context. If it does, - it must be a WASM-friendly value, as it will be returning from - a call made from WASM code. - - - contextKey (function): is only used if bindScope is 'context' - or if bindScope is not set and this function is, in which case - a bindScope of 'context' is assumed. This function gets bound - to this object, so its "this" is this object. It gets passed - (argv,argIndex), where argIndex is the index of _this_ function - in its _wrapping_ function's arguments, and argv is the - _current_ still-being-xWrap()-processed args array. (Got all - that?) When thisFunc(argv,argIndex) is called by xWrap(), all - arguments in argv to the left of argIndex will have been - processed by xWrap() by the time this is called. argv[argIndex] - will be the value the user passed in to the xWrap()'d function - for the argument this FuncPtrAdapter is mapped to. Arguments to - the right of argv[argIndex] will not yet have been converted - before this is called. The function must return a key which - uniquely identifies this function mapping context for _this_ - FuncPtrAdapter instance (other instances are not considered), - taking into account that C functions often take some sort of - state object as one or more of their arguments. As an example, - if the xWrap()'d function takes `(int,T*,functionPtr,X*)` then - this FuncPtrAdapter instance is argv[2], and contextKey(argv,2) - might return 'T@'+argv[1], or even just argv[1]. Note, - however, that the (`X*`) argument will not yet have been - processed by the time this is called and should not be used as - part of that key because its pre-conversion data type might be - unpredictable. Similarly, care must be taken with C-string-type - arguments: those to the left in argv will, when this is called, - be WASM pointers, whereas those to the right might (and likely - do) have another data type. When using C-strings in keys, never - use their pointers in the key because most C-strings in this - constellation are transient. Conversely, the pointer address - makes an ideal key for longer-lived native pointer types. - - Yes, that ^^^ is quite awkward, but it's what we have. In - context, as it were, it actually makes some sense, but one must - look under its hook a bit to understand why it's shaped the - way it is. - - The constructor only saves the above state for later, and does - not actually bind any functions. The conversion, if any, is - performed when its convertArg() method is called via xWrap(). - - Shortcomings: - - - These "reverse" bindings, i.e. calling into a JS-defined - function from a WASM-defined function (the generated proxy - wrapper), lack all type conversion support. That means, for - example, that... - - - Function pointers which include C-string arguments may still - need a level of hand-written wrappers around them, depending on - how they're used, in order to provide the client with JS - strings. Alternately, clients will need to perform such - conversions on their own, e.g. using cstrToJs(). The purpose of - the callProxy() method is to account for such cases. - */ - xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter { - constructor(opt) { - super(opt); - if (xArg.FuncPtrAdapter.warnOnUse) console.warn("xArg.FuncPtrAdapter is an internal-only API", "and is not intended to be invoked from", "client-level code. Invoked with:", opt); - this.name = opt.name || "unnamed"; - this.signature = opt.signature; - if (opt.contextKey instanceof Function) { - this.contextKey = opt.contextKey; - if (!opt.bindScope) opt.bindScope = "context"; - } - this.bindScope = opt.bindScope || toss("FuncPtrAdapter options requires a bindScope (explicit or implied)."); - if (FuncPtrAdapter.bindScopes.indexOf(opt.bindScope) < 0) toss("Invalid options.bindScope (" + opt.bindMod + ") for FuncPtrAdapter. Expecting one of: (" + FuncPtrAdapter.bindScopes.join(", ") + ")"); - this.isTransient = "transient" === this.bindScope; - this.isContext = "context" === this.bindScope; - this.isPermanent = "permanent" === this.bindScope; - this.singleton = "singleton" === this.bindScope ? [] : void 0; - this.callProxy = opt.callProxy instanceof Function ? opt.callProxy : void 0; - } - /** - The static class members are defined outside of the class to - work around an emcc toolchain build problem: one of the tools - in emsdk v3.1.42 does not support the static keyword. - */ - contextKey(argv, argIndex) { - return this; - } - /** - Returns this object's mapping for the given context key, in the - form of an an array, creating the mapping if needed. The key - may be anything suitable for use in a Map. - - The returned array is intended to be used as a pair of - [JSValue, WasmFuncPtr], where the first element is one passed - to this.convertArg() and the second is its WASM form. - */ - contextMap(key) { - const cm = this.__cmap || (this.__cmap = /* @__PURE__ */ new Map()); - let rc = cm.get(key); - if (void 0 === rc) cm.set(key, rc = []); - return rc; - } - /** - Gets called via xWrap() to "convert" v to a WASM-bound function - pointer. If v is one of (a WASM pointer, null, undefined) then - (v||0) is returned and any earlier function installed by this - mapping _might_, depending on how it's bound, be uninstalled. - If v is not one of those types, it must be a Function, for - which this method creates (if needed) a WASM function binding - and returns the WASM pointer to that binding. - - If this instance is not in 'transient' mode, it will remember - the binding for at least the next call, to avoid recreating the - function binding unnecessarily. - - If it's passed a pointer(ish) value for v, it assumes it's a - WASM function pointer and does _not_ perform any function - binding, so this object's bindMode is irrelevant/ignored for - such cases. - - See the parent class's convertArg() docs for details on what - exactly the 2nd and 3rd arguments are. - */ - convertArg(v, argv, argIndex) { - let pair = this.singleton; - if (!pair && this.isContext) pair = this.contextMap(this.contextKey(argv, argIndex)); - if (pair && 2 === pair.length && pair[0] === v) return pair[1]; - if (v instanceof Function) { - if (this.callProxy) v = this.callProxy(v); - const fp = __installFunction(v, this.signature, this.isTransient); - if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this, this.contextKey(argv, argIndex), "@" + fp, v); - if (pair) { - if (pair[1]) { - if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); - try { - cache.scopedAlloc.pushPtr(pair[1]); - } catch (e) {} - } - pair[0] = arguments[0] || __NullPtr; - pair[1] = fp; - } - return fp; - } else if (target.isPtr(v) || null === v || void 0 === v) { - if (pair && pair[1] && pair[1] !== v) { - if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v); - try { - cache.scopedAlloc.pushPtr(pair[1]); - } catch (e) {} - pair[0] = pair[1] = v || __NullPtr; - } - return v || __NullPtr; - } else throw new TypeError("Invalid FuncPtrAdapter argument type. Expecting a function pointer or a " + (this.name ? this.name + " " : "") + "function matching signature " + this.signature + "."); - } - }; - /** If true, the constructor emits a warning. The intent is that - this be set to true after bootstrapping of the higher-level - client library is complete, to warn downstream clients that - they shouldn't be relying on this implementation detail which - does not have a stable interface. */ - xArg.FuncPtrAdapter.warnOnUse = false; - /** If true, convertArg() will call FuncPtrAdapter.debugOut() when - it (un)installs a function binding to/from WASM. Note that - deinstallation of bindScope=transient bindings happens via - scopedAllocPop() so will not be output. */ - xArg.FuncPtrAdapter.debugFuncInstall = false; - /** Function used for debug output. */ - xArg.FuncPtrAdapter.debugOut = console.debug.bind(console); - /** - List of legal values for the FuncPtrAdapter bindScope config - option. - */ - xArg.FuncPtrAdapter.bindScopes = [ - "transient", - "context", - "singleton", - "permanent" - ]; - /** Throws if xArg.get(t) returns falsy. */ - const __xArgAdapterCheck = (t) => xArg.get(t) || toss("Argument adapter not found:", t); - /** Throws if xResult.get(t) returns falsy. */ - const __xResultAdapterCheck = (t) => xResult.get(t) || toss("Result adapter not found:", t); - /** - Fetches the xWrap() argument adapter mapped to t, calls it, - passing in all remaining arguments, and returns the result. - Throws if t is not mapped to an argument converter. - */ - cache.xWrap.convertArg = (t, ...args) => __xArgAdapterCheck(t)(...args); - /** - Identical to convertArg() except that it does not perform - an is-defined check on the mapping to t before invoking it. - */ - cache.xWrap.convertArgNoCheck = (t, ...args) => xArg.get(t)(...args); - /** - Fetches the xWrap() result adapter mapped to t, calls it, passing - it v, and returns the result. Throws if t is not mapped to an - argument converter. - */ - cache.xWrap.convertResult = (t, v) => null === t ? v : t ? __xResultAdapterCheck(t)(v) : void 0; - /** - Identical to convertResult() except that it does not perform an - is-defined check on the mapping to t before invoking it. - */ - cache.xWrap.convertResultNoCheck = (t, v) => null === t ? v : t ? xResult.get(t)(v) : void 0; - /** - Creates a wrapper for another function which converts the arguments - of the wrapper to argument types accepted by the wrapped function, - then converts the wrapped function's result to another form - for the wrapper. - - The first argument must be one of: - - - A JavaScript function. - - The name of a WASM-exported function. xGet() is used to fetch - the exported function, which throws if it's not found. - - A pointer into the indirect function table. e.g. a pointer - returned from target.installFunction(). - - It returns either the passed-in function or a wrapper for that - function which converts the JS-side argument types into WASM-side - types and converts the result type. - - The second argument, `resultType`, describes the conversion for - the wrapped functions result. A literal `null` or the string - `'null'` both mean to return the original function's value as-is - (mnemonic: there is "null" conversion going on). Literal - `undefined` or the string `"void"` both mean to ignore the - function's result and return `undefined`. Aside from those two - special cases, the `resultType` value may be one of the values - described below or any mapping installed by the client using - xWrap.resultAdapter(). - - If passed 3 arguments and the final one is an array, that array - must contain a list of type names (see below) for adapting the - arguments from JS to WASM. If passed 2 arguments, more than 3, - or the 3rd is not an array, all arguments after the 2nd (if any) - are treated as type names. i.e.: - - ``` - xWrap('funcname', 'i32', 'string', 'f64'); - // is equivalent to: - xWrap('funcname', 'i32', ['string', 'f64']); - ``` - - This function enforces that the given list of arguments has the - same arity as the being-wrapped function (as defined by its - `length` property) and it will throw if that is not the case. - Similarly, the created wrapper will throw if passed a differing - argument count. The intent of that strictness is to help catch - coding errors in using JS-bound WASM functions earlier rather - than laer. - - Type names are symbolic names which map the arguments to an - adapter function to convert, if needed, the value before passing - it on to WASM or to convert a return result from WASM. The list - of pre-defined names: - - - `i8`, `i16`, `i32` (args and results): all integer conversions - which convert their argument to an integer and truncate it to - the given bit length. - - - `*`, `**`, and `pointer` (args): are assumed to be WASM pointer - values and are returned coerced to an appropriately-sized - pointer value (i32 or i64). Non-numeric values will coerce to 0 - and out-of-range values will have undefined results (just as - with any pointer misuse). - - - `*` and `pointer` (results): aliases for the current - WASM pointer numeric type. - - - `**` (args): is simply a descriptive alias for the WASM pointer - type. It's primarily intended to mark output-pointer arguments, - noting that JS's view of WASM does not distinguish between - pointers and pointers-to-pointers, so all such interpretation - of `**`, as distinct from `*`, necessarily happens at the - client level. - - - `NumType*` (args): a type name in this form, where T is - the name of a numeric mapping, e.g. 'int16' or 'double', - is treated like `*`. - - - `i64` (args and results): passes the value to BigInt() to - convert it to an int64. This conversion will if bigIntEnabled - is falsy. - - - `f32` (`float`), `f64` (`double`) (args and results): pass - their argument to Number(). i.e. the adapter does not currently - distinguish between the two types of floating-point numbers. - - - `number` (results): converts the result to a JS Number using - Number(theValue). This is for result conversions only, as it's - not possible to generically know which type of number to - convert arguments to. - - Non-numeric conversions include: - - - `null` literal or `"null"` string (args and results): perform - no translation and pass the arg on as-is. This is primarily - useful for results but may have a use or two for arguments. - - - `string` or `utf8` (args): has two different semantics in order - to accommodate various uses of certain C APIs - (e.g. output-style strings)... - - - If the arg is a JS string, it creates a _temporary_ - UTF-8-encoded C-string to pass to the exported function, - cleaning it up before the wrapper returns. If a long-lived - C-string pointer is required, that requires client-side code - to create the string then pass its pointer to the function. - - - Else the arg is assumed to be a pointer to a string the - client has already allocated and it's passed on as - a WASM pointer. - - - `string` or `utf8` (results): treats the result value as a - const C-string, encoded as UTF-8, copies it to a JS string, - and returns that JS string. - - - `string:dealloc` or `utf8:dealloc` (results): treats the result - value as a non-const UTF-8 C-string, ownership of which has - just been transfered to the caller. It copies the C-string to a - JS string, frees the C-string using dealloc(), and returns the - JS string. If such a result value is NULL, the JS result is - `null`. Achtung: when using an API which returns results from a - specific allocator, e.g. `my_malloc()`, this conversion _is not - legal_. Instead, an equivalent conversion which uses the - appropriate deallocator is required. For example: - - ```js - target.xWrap.resultAdapter('string:my_free',(i)=>{ - try { return i ? target.cstrToJs(i) : null; } - finally{ target.exports.my_free(i); } - }; - ``` - - - `json` (results): treats the result as a const C-string and - returns the result of passing the converted-to-JS string to - JSON.parse(). Returns `null` if the C-string is a NULL pointer. - - - `json:dealloc` (results): works exactly like `string:dealloc` but - returns the same thing as the `json` adapter. Note the - warning in `string:dealloc` regarding matching allocators and - deallocators. - - The type names for results and arguments are validated when - xWrap() is called and any unknown names will trigger an - exception. - - Clients may map their own result and argument adapters using - xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all - type conversions are valid for both arguments _and_ result types - as they often have different memory ownership requirements. - - Design note: the ability to pass in a JS function as the first - argument is of relatively limited use, primarily for testing - argument and result converters. JS functions, by and large, will - not want to deal with C-type arguments. - - TODOs: - - - Figure out how/whether we can (semi-)transparently handle - pointer-type _output_ arguments. Those currently require - explicit handling by allocating pointers, assigning them before - the call using poke(), and fetching them with - peek() after the call. We may be able to automate some - or all of that. - - - Figure out whether it makes sense to extend the arg adapter - interface such that each arg adapter gets an array containing - the results of the previous arguments in the current call. That - might allow some interesting type-conversion feature. Use case: - handling of the final argument to sqlite3_prepare_v2() depends - on the type (pointer vs JS string) of its 2nd - argument. Currently that distinction requires hand-writing a - wrapper for that function. That case is unusual enough that - abstracting it into this API (and taking on the associated - costs) may well not make good sense. - */ - target.xWrap = function callee(fArg, resultType, ...argTypes) { - if (3 === arguments.length && Array.isArray(arguments[2])) argTypes = arguments[2]; - if (target.isPtr(fArg)) fArg = target.functionEntry(fArg) || toss("Function pointer not found in WASM function table."); - const fIsFunc = fArg instanceof Function; - const xf = fIsFunc ? fArg : target.xGet(fArg); - if (fIsFunc) fArg = xf.name || "unnamed function"; - if (argTypes.length !== xf.length) __argcMismatch(fArg, xf.length); - if (0 === xf.length && (null === resultType || "null" === resultType)) return xf; - __xResultAdapterCheck(resultType); - for (const t of argTypes) if (t instanceof AbstractArgAdapter) xArg.set(t, (...args) => t.convertArg(...args)); - else __xArgAdapterCheck(t); - const cxw = cache.xWrap; - if (0 === xf.length) return (...args) => args.length ? __argcMismatch(fArg, xf.length) : cxw.convertResult(resultType, xf.call(null)); - return function(...args) { - if (args.length !== xf.length) __argcMismatch(fArg, xf.length); - const scope = target.scopedAllocPush(); - try { - let i = 0; - if (callee.debug) console.debug("xWrap() preparing: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); - for (; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], args, i); - if (callee.debug) console.debug("xWrap() calling: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args); - return cxw.convertResultNoCheck(resultType, xf.apply(null, args)); - } finally { - target.scopedAllocPop(scope); - } - }; - }; - /** - Internal impl for xWrap.resultAdapter() and argAdapter(). - - func = one of xWrap.resultAdapter or xWrap.argAdapter. - - argc = the number of args in the wrapping call to this - function. - - typeName = the first arg to the wrapping function. - - adapter = the second arg to the wrapping function. - - modeName = a descriptive name of the wrapping function for - error-reporting purposes. - - xcvPart = one of xResult or xArg. - - This acts as either a getter (if 1===argc) or setter (if - 2===argc) for the given adapter. Returns func on success or - throws if (A) called with 2 args but adapter is-not-a Function or - (B) typeName is not a string or (C) argc is not one of (1, 2). - */ - const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart) { - if ("string" === typeof typeName) { - if (1 === argc) return xcvPart.get(typeName); - else if (2 === argc) { - if (!adapter) { - xcvPart.delete(typeName); - return func; - } else if (!(adapter instanceof Function)) toss(modeName, "requires a function argument."); - xcvPart.set(typeName, adapter); - return func; - } - } - toss("Invalid arguments to", modeName); - }; - /** - Gets, sets, or removes a result value adapter for use with - xWrap(). If passed only 1 argument, the adapter function for the - given type name is returned. If the second argument is explicit - falsy (as opposed to defaulted), the adapter named by the first - argument is removed. If the 2nd argument is not falsy, it must be - a function which takes one value and returns a value appropriate - for the given type name. The adapter may throw if its argument is - not of a type it can work with. This function throws for invalid - arguments. - - Example: - - ``` - xWrap.resultAdapter('twice',(v)=>v+v); - ``` - - Result adapters MUST NOT use the scopedAlloc() family of APIs to - allocate a result value. xWrap()-generated wrappers run in the - context of scopedAllocPush() so that argument adapters can easily - convert, e.g., to C-strings, and have them cleaned up - automatically before the wrapper returns to the caller. Likewise, - if a _result_ adapter uses scoped allocation, the result will be - freed before the wrapper returns, leading to chaos and undefined - behavior. - - When called as a setter, this function returns itself. - */ - target.xWrap.resultAdapter = function f(typeName, adapter) { - return __xAdapter(f, arguments.length, typeName, adapter, "resultAdapter()", xResult); - }; - /** - Functions identically to xWrap.resultAdapter() but applies to - call argument conversions instead of result value conversions. - - xWrap()-generated wrappers perform argument conversion in the - context of a scopedAllocPush(), so any memory allocation - performed by argument adapters really, really, really should be - made using the scopedAlloc() family of functions unless - specifically necessary. For example: - - ``` - xWrap.argAdapter('my-string', function(v){ - return ('string'===typeof v) - ? myWasmObj.scopedAllocCString(v) : null; - }; - ``` - - Contrariwise, _result_ adapters _must not_ use scopedAlloc() to - allocate results because they would be freed before the - xWrap()-created wrapper returns. - - It is perfectly legitimate to use these adapters to perform - argument validation, as opposed (or in addition) to conversion. - When used that way, they should throw for invalid arguments. - */ - target.xWrap.argAdapter = function f(typeName, adapter) { - return __xAdapter(f, arguments.length, typeName, adapter, "argAdapter()", xArg); - }; - target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter; - /** - Functions like xCall() but performs argument and result type - conversions as for xWrap(). The first, second, and third - arguments are as documented for xWrap(), except that the 3rd - argument may be either a falsy value or empty array to represent - nullary functions. The 4th+ arguments are arguments for the call, - with the special case that if the 4th argument is an array, it is - used as the arguments for the call. Returns the converted result - of the call. - - This is just a thin wrapper around xWrap(). If the given function - is to be called more than once, it's more efficient to use - xWrap() to create a wrapper, then to call that wrapper as many - times as needed. For one-shot calls, however, this variant is - simpler. - */ - target.xCallWrapped = function(fArg, resultType, argTypes, ...args) { - if (Array.isArray(arguments[3])) args = arguments[3]; - return target.xWrap(fArg, resultType, argTypes || []).apply(null, args || []); - }; - /** - This function is ONLY exposed in the public API to facilitate - testing. It should not be used in application-level code, only - in test code. - - Expects to be given (typeName, value) and returns a conversion - of that value as has been registered using argAdapter(). - It throws if no adapter is found. - - ACHTUNG: the adapter may require that a scopedAllocPush() is - active and it may allocate memory within that scope. It may also - require additional arguments, depending on the type of - conversion. - */ - target.xWrap.testConvertArg = cache.xWrap.convertArg; - /** - This function is ONLY exposed in the public API to facilitate - testing. It should not be used in application-level code, only - in test code. - - Expects to be given (typeName, value) and returns a conversion - of that value as has been registered using resultAdapter(). - It throws if no adapter is found. - - ACHTUNG: the adapter may allocate memory which the caller may need - to know how to free. - */ - target.xWrap.testConvertResult = cache.xWrap.convertResult; - return target; - }; - /** - yawl (Yet Another Wasm Loader) provides very basic wasm loader. - It requires a config object: - - - `uri`: required URI of the WASM file to load. - - - `onload(loadResult)`: optional callback. Its argument is an - object described in more detail below. - - - `imports`: optional imports object for - WebAssembly.instantiate[Streaming](). The default is an empty - set of imports. If the module requires any imports, this object - must include them. - - - `wasmUtilTarget`: optional object suitable for passing to - WhWasmUtilInstaller(). If set, it gets passed to that function - before the returned promise resolves. This function sets several - properties on it before passing it on to that function (which - sets many more): - - - `module`, `instance`: the properties from the - instantiate[Streaming]() result. - - - If `instance.exports.memory` is _not_ set then it requires that - `config.imports.env.memory` be set (else it throws), and - assigns that to `wasmUtilTarget.memory`. - - - If `wasmUtilTarget.alloc` is not set and - `instance.exports.malloc` is, it installs - `wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()` - wrappers for the exports' `malloc` and `free` functions - if exports.malloc exists. - - It returns a function which, when called, initiates loading of the - module and returns a Promise. When that Promise resolves, it calls - the `config.onload` callback (if set) and passes it `(loadResult)`, - where `loadResult` is derived from the result of - WebAssembly.instantiate[Streaming](), an object in the form: - - ``` - { - module: a WebAssembly.Module, - instance: a WebAssembly.Instance, - config: the config arg to this function - } - ``` - - (The initial `then()` attached to the promise gets only that - object, and not the `config` object, thus the potential need for a - `config.onload` handler.) - - Error handling is up to the caller, who may attach a `catch()` call - to the promise. - */ - globalThis.WhWasmUtilInstaller.yawl = function yawl(config) { - "use strict"; - const wfetch = () => fetch(config.uri, { credentials: "same-origin" }); - const wui = this; - const finalThen = function(arg) { - if (config.wasmUtilTarget) { - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - const tgt = config.wasmUtilTarget; - tgt.module = arg.module; - tgt.instance = arg.instance; - if (!tgt.instance.exports.memory) - /** - WhWasmUtilInstaller requires either tgt.exports.memory - (exported from WASM) or tgt.memory (JS-provided memory - imported into WASM). - */ - tgt.memory = config?.imports?.env?.memory || toss("Missing 'memory' object!"); - if (!tgt.alloc && arg.instance.exports.malloc) { - const exports = arg.instance.exports; - tgt.alloc = function(n) { - return exports.malloc(n) || toss("Allocation of", n, "bytes failed."); - }; - tgt.dealloc = function(m) { - m && exports.free(m); - }; - } - wui(tgt); - } - arg.config = config; - if (config.onload) config.onload(arg); - return arg; - }; - return WebAssembly.instantiateStreaming ? () => WebAssembly.instantiateStreaming(wfetch(), config.imports || {}).then(finalThen) : () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, config.imports || {})).then(finalThen); - }.bind(globalThis.WhWasmUtilInstaller); - globalThis.Jaccwabyt = function StructBinderFactory(config) { - "use strict"; - /** Throws a new Error, the message of which is the concatenation - all args with a space between each. */ - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - { - let h = config.heap; - if (h instanceof WebAssembly.Memory) h = function() { - return new Uint8Array(this.buffer); - }.bind(h); - else if (!(h instanceof Function)) toss("config.heap must be WebAssembly.Memory instance or", "a function which returns one."); - config.heap = h; - } - ["alloc", "dealloc"].forEach(function(k) { - config[k] instanceof Function || toss("Config option '" + k + "' must be a function."); - }); - const SBF = StructBinderFactory, heap = config.heap, alloc = config.alloc, dealloc = config.dealloc; - config.realloc; - const log = config.log || console.debug.bind(console), memberPrefix = config.memberPrefix || "", memberSuffix = config.memberSuffix || "", BigInt = globalThis["BigInt"], BigInt64Array = globalThis["BigInt64Array"], bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array; - let ptr; - const ptrSize = config.pointerSize || config.ptrSize || ("bigint" === typeof (ptr = alloc(1)) ? 8 : 4); - const ptrIR = config.pointerIR || config.ptrIR || (4 === ptrSize ? "i32" : "i64"); - if (ptr) { - dealloc(ptr); - ptr = void 0; - } - if (ptrSize !== 4 && ptrSize !== 8) toss("Invalid pointer size:", ptrSize); - if (ptrIR !== "i32" && ptrIR !== "i64") toss("Invalid pointer representation:", ptrIR); - /** Either BigInt or, if !bigIntEnabled, a function which - throws complaining that BigInt is not enabled. */ - const __BigInt = bigIntEnabled && BigInt ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build."); - const __asPtrType = "i32" == ptrIR ? Number : __BigInt; - const __NullPtr = __asPtrType(0); - /** - Expects any number of numeric arguments, each one of either type - Number or BigInt. It sums them up (from an implicit starting - point of 0 or 0n) and returns them as a number of the same type - which target.asPtrType() uses. - - This is a workaround for not being able to mix Number/BigInt in - addition/subtraction expressions (which we frequently need for - calculating pointer offsets). - */ - const __ptrAdd = function(...args) { - let rc = __NullPtr; - for (let i = 0; i < args.length; ++i) rc += __asPtrType(args[i]); - return rc; - }; - const __ptrAddSelf = function(...args) { - return __ptrAdd(this.pointer, ...args); - }; - if (!SBF.debugFlags) { - SBF.__makeDebugFlags = function(deriveFrom = null) { - if (deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags; - const f = function f(flags) { - if (0 === arguments.length) return f.__flags; - if (flags < 0) { - delete f.__flags.getter; - delete f.__flags.setter; - delete f.__flags.alloc; - delete f.__flags.dealloc; - } else { - f.__flags.getter = 0 !== (1 & flags); - f.__flags.setter = 0 !== (2 & flags); - f.__flags.alloc = 0 !== (4 & flags); - f.__flags.dealloc = 0 !== (8 & flags); - } - return f._flags; - }; - Object.defineProperty(f, "__flags", { - iterable: false, - writable: false, - value: Object.create(deriveFrom) - }); - if (!deriveFrom) f(0); - return f; - }; - SBF.debugFlags = SBF.__makeDebugFlags(); - } - const isLittleEndian = true; - /** - Some terms used in the internal docs: - - StructType: a struct-wrapping class generated by this - framework. - - DEF: struct description object. - - SIG: struct member signature string. - */ - /** True if SIG s looks like a function signature, else - false. */ - const isFuncSig = (s) => "(" === s[1]; - /** True if SIG s is-a pointer-type signature. */ - const isPtrSig = (s) => "p" === s || "P" === s || "s" === s; - const isAutoPtrSig = (s) => "P" === s; - /** Returns p if SIG s is a function SIG, else returns s[0]. */ - const sigLetter = (s) => s ? isFuncSig(s) ? "p" : s[0] : void 0; - /** Returns the WASM IR form of the letter at SIG s[0]. Throws for - an unknown SIG. */ - const sigIR = function(s) { - switch (sigLetter(s)) { - case "c": - case "C": return "i8"; - case "i": return "i32"; - case "p": - case "P": - case "s": return ptrIR; - case "j": return "i64"; - case "f": return "float"; - case "d": return "double"; - } - toss("Unhandled signature IR:", s); - }; - /** Returns the WASM sizeof of the letter at SIG s[0]. Throws for an - unknown SIG. */ - const sigSize = function(s) { - switch (sigLetter(s)) { - case "c": - case "C": return 1; - case "i": return 4; - case "p": - case "P": - case "s": return ptrSize; - case "j": return 8; - case "f": return 4; - case "d": return 8; - } - toss("Unhandled signature sizeof:", s); - }; - const affirmBigIntArray = BigInt64Array ? () => true : () => toss("BigInt64Array is not available."); - /** Returns the name of a DataView getter method corresponding - to the given SIG. */ - const sigDVGetter = function(s) { - switch (sigLetter(s)) { - case "p": - case "P": - case "s": - switch (ptrSize) { - case 4: return "getInt32"; - case 8: return affirmBigIntArray() && "getBigInt64"; - } - break; - case "i": return "getInt32"; - case "c": return "getInt8"; - case "C": return "getUint8"; - case "j": return affirmBigIntArray() && "getBigInt64"; - case "f": return "getFloat32"; - case "d": return "getFloat64"; - } - toss("Unhandled DataView getter for signature:", s); - }; - /** Returns the name of a DataView setter method corresponding - to the given SIG. */ - const sigDVSetter = function(s) { - switch (sigLetter(s)) { - case "p": - case "P": - case "s": - switch (ptrSize) { - case 4: return "setInt32"; - case 8: return affirmBigIntArray() && "setBigInt64"; - } - break; - case "i": return "setInt32"; - case "c": return "setInt8"; - case "C": return "setUint8"; - case "j": return affirmBigIntArray() && "setBigInt64"; - case "f": return "setFloat32"; - case "d": return "setFloat64"; - } - toss("Unhandled DataView setter for signature:", s); - }; - /** - Returns a factory for either Number or BigInt, depending on the - given SIG. This constructor is used in property setters to coerce - the being-set value to the correct pointer size. - */ - const sigDVSetWrapper = function(s) { - switch (sigLetter(s)) { - case "i": - case "f": - case "c": - case "C": - case "d": return Number; - case "j": return __BigInt; - case "p": - case "P": - case "s": - switch (ptrSize) { - case 4: return Number; - case 8: return __BigInt; - } - break; - } - toss("Unhandled DataView set wrapper for signature:", s); - }; - /** Returns the given struct and member name in a form suitable for - debugging and error output. */ - const sPropName = (s, k) => s + "::" + k; - const __propThrowOnSet = function(structName, propName) { - return () => toss(sPropName(structName, propName), "is read-only."); - }; - /** - In order to completely hide StructBinder-bound struct pointers - from JS code, we store them in a scope-local WeakMap which maps - the struct-bound objects to an object with their metadata: - - { - .p = the native pointer, - .o = self (for an eventual reverse-mapping), - .xb = extra bytes allocated for p, - .zod = zeroOnDispose, - .ownsPointer = true if this object owns p - } - - The .p data are accessible via obj.pointer, which is gated behind - a property interceptor, but are not exposed anywhere else in the - public API. - */ - const getInstanceHandle = function f(obj, create = true) { - let ii = f.map.get(obj); - if (!ii && create) f.map.set(obj, ii = f.create(obj)); - return ii; - }; - getInstanceHandle.map = /* @__PURE__ */ new WeakMap(); - getInstanceHandle.create = (forObj) => { - return Object.assign(Object.create(null), { - o: forObj, - p: void 0, - ownsPointer: false, - zod: false, - xb: 0 - }); - }; - /** - Remove the getInstanceHandle() mapping for obj. - */ - const rmInstanceHandle = (obj) => getInstanceHandle.map.delete(obj); - const __isPtr32 = (ptr) => "number" === typeof ptr && ptr === (ptr | 0) && ptr >= 0; - const __isPtr64 = (ptr) => "bigint" === typeof ptr && ptr >= 0 || __isPtr32(ptr); - /** - isPtr() is an alias for isPtr32() or isPtr64(), depending on - ptrSize. - */ - const __isPtr = 4 === ptrSize ? __isPtr32 : __isPtr64; - const __isNonNullPtr = (v) => __isPtr(v) && v > 0; - /** Frees the obj.pointer memory (a.k.a. m), handles obj.ondispose, - and unmaps obj from its native resources. */ - const __freeStruct = function(ctor, obj, m) { - const ii = getInstanceHandle(obj, false); - if (!ii) return; - rmInstanceHandle(obj); - if (!m && !(m = ii.p)) { - console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo"); - return; - } - if (Array.isArray(obj.ondispose)) { - let x; - while (x = obj.ondispose.pop()) try { - if (x instanceof Function) x.call(obj); - else if (x instanceof StructType) x.dispose(); - else if (__isPtr(x)) dealloc(x); - } catch (e) { - console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); - } - } else if (obj.ondispose instanceof Function) try { - obj.ondispose(); - } catch (e) { - console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e); - } - delete obj.ondispose; - if (ctor.debugFlags.__flags.dealloc) log("debug.dealloc:", ii.ownsPointer ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + m); - if (ii.ownsPointer) { - if (ii.zod || ctor.structInfo.zeroOnDispose) heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof + ii.xb); - dealloc(m); - } - }; - /** Returns a skeleton for a read-only, non-iterable property - * descriptor. */ - const rop0 = () => { - return { - configurable: false, - writable: false, - iterable: false - }; - }; - /** Returns a skeleton for a read-only property accessor wrapping - value v. */ - const rop = (v) => { - return { - ...rop0(), - value: v - }; - }; - /** Allocates obj's memory buffer based on the size defined in - ctor.structInfo.sizeof. */ - const __allocStruct = function f(ctor, obj, xm) { - let opt; - const checkPtr = (ptr) => { - __isNonNullPtr(ptr) || toss("Invalid pointer value", arguments[0], "for", ctor.structName, "constructor."); - }; - if (arguments.length >= 3) if (xm && "object" === typeof xm) { - opt = xm; - xm = opt?.wrap; - } else { - checkPtr(xm); - opt = { wrap: xm }; - } - else opt = {}; - const fill = !xm; - let nAlloc = 0; - let ownsPointer = false; - if (xm) { - checkPtr(xm); - ownsPointer = !!opt?.takeOwnership; - } else { - const nX = opt?.extraBytes ?? 0; - if (nX < 0 || nX !== (nX | 0)) toss("Invalid extraBytes value:", opt?.extraBytes); - nAlloc = ctor.structInfo.sizeof + nX; - xm = alloc(nAlloc) || toss("Allocation of", ctor.structName, "structure failed."); - ownsPointer = true; - } - try { - if (opt?.debugFlags) obj.debugFlags(opt.debugFlags); - if (ctor.debugFlags.__flags.alloc) log("debug.alloc:", fill ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + xm); - if (fill) heap().fill(0, Number(xm), Number(xm) + nAlloc); - const ii = getInstanceHandle(obj); - ii.p = xm; - ii.ownsPointer = ownsPointer; - ii.xb = nAlloc ? nAlloc - ctor.structInfo.sizeof : 0; - ii.zod = !!opt?.zeroOnDispose; - if (opt?.ondispose && opt.ondispose !== xm) obj.addOnDispose(opt.ondispose); - } catch (e) { - __freeStruct(ctor, obj, xm); - throw e; - } - }; - /** True if sig looks like an emscripten/jaccwabyt - type signature, else false. */ - const looksLikeASig = function f(sig) { - f.rxSig1 ??= /^[ipPsjfdcC]$/; - f.rxSig2 ??= /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/; - return f.rxSig1.test(sig) || f.rxSig2.test(sig); - }; - /** Returns a pair of adaptor maps (objects) in a length-3 - array specific to the given object. */ - const __adaptorsFor = function(who) { - let x = this.get(who); - if (!x) { - x = [ - Object.create(null), - Object.create(null), - Object.create(null) - ]; - this.set(who, x); - } - return x; - }.bind(/* @__PURE__ */ new WeakMap()); - /** Code de-duplifier for __adaptGet(), __adaptSet(), and - __adaptStruct(). */ - const __adaptor = function(who, which, key, proxy) { - const a = __adaptorsFor(who)[which]; - if (3 === arguments.length) return a[key]; - if (proxy) return a[key] = proxy; - return delete a[key]; - }; - const __adaptGet = function(key, ...args) { - return __adaptor(this, 0, key, ...args); - }; - const __affirmNotASig = function(ctx, key) { - looksLikeASig(key) && toss(ctx, "(", key, ") collides with a data type signature."); - }; - const __adaptSet = function(key, ...args) { - __affirmNotASig("Setter adaptor", key); - return __adaptor(this, 1, key, ...args); - }; - const __adaptStruct = function(key, ...args) { - __affirmNotASig("Struct adaptor", key); - return __adaptor(this, 2, key, ...args); - }; - /** - An internal counterpart of __adaptStruct(). If key is-a string, - uses __adaptor(who) to fetch the struct-adaptor entry for key, - else key is assumed to be a struct description object. If it - resolves to an object, that's returned, else an exception is - thrown. - */ - const __adaptStruct2 = function(who, key) { - const si = "string" === typeof key ? __adaptor(who, 2, key) : key; - if ("object" !== typeof si) toss("Invalid struct mapping object. Arg =", key, JSON.stringify(si)); - return si; - }; - const __memberKey = (k) => memberPrefix + k + memberSuffix; - const __memberKeyProp = rop(__memberKey); - /** - Looks up a struct member in structInfo.members. Throws if found - if tossIfNotFound is true, else returns undefined if not - found. The given name may be either the name of the - structInfo.members key (faster) or the key as modified by the - memberPrefix and memberSuffix settings. - */ - const __lookupMember = function(structInfo, memberName, tossIfNotFound = true) { - let m = structInfo.members[memberName]; - if (!m && (memberPrefix || memberSuffix)) { - for (const v of Object.values(structInfo.members)) if (v.key === memberName) { - m = v; - break; - } - if (!m && tossIfNotFound) toss(sPropName(structInfo.name || structInfo.structName, memberName), "is not a mapped struct member."); - } - return m; - }; - /** - Uses __lookupMember(obj.structInfo,memberName) to find a member, - throwing if not found. Returns its signature, either in this - framework's native format or in Emscripten format. - */ - const __memberSignature = function f(obj, memberName, emscriptenFormat = false) { - if (!f._) f._ = (x) => x.replace(/[^vipPsjrdcC]/g, "").replace(/[pPscC]/g, "i"); - const m = __lookupMember(obj.structInfo, memberName, true); - return emscriptenFormat ? f._(m.signature) : m.signature; - }; - /** Impl of X.memberKeys() for StructType and struct ctors. */ - const __structMemberKeys = rop(function() { - const a = []; - for (const k of Object.keys(this.structInfo.members)) a.push(this.memberKey(k)); - return a; - }); - const __utf8Decoder = new TextDecoder("utf-8"); - const __utf8Encoder = new TextEncoder(); - /** Internal helper to use in operations which need to distinguish - between SharedArrayBuffer heap memory and non-shared heap. */ - const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer; - const __utf8Decode = function(arrayBuffer, begin, end) { - if (8 === ptrSize) { - begin = Number(begin); - end = Number(end); - } - return __utf8Decoder.decode(arrayBuffer.buffer instanceof __SAB ? arrayBuffer.slice(begin, end) : arrayBuffer.subarray(begin, end)); - }; - /** - Uses __lookupMember() to find the given obj.structInfo key. - Returns that member if it is a string, else returns false. If the - member is not found, throws if tossIfNotFound is true, else - returns false. - */ - const __memberIsString = function(obj, memberName, tossIfNotFound = false) { - const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound); - return m && 1 === m.signature.length && "s" === m.signature[0] ? m : false; - }; - /** - Given a member description object, throws if member.signature is - not valid for assigning to or interpretation as a C-style string. - It optimistically assumes that any signature of (i,p,s) is - C-string compatible. - */ - const __affirmCStringSignature = function(member) { - if ("s" === member.signature) return; - toss("Invalid member type signature for C-string value:", JSON.stringify(member)); - }; - /** - Looks up the given member in obj.structInfo. If it has a - signature of 's' then it is assumed to be a C-style UTF-8 string - and a decoded copy of the string at its address is returned. If - the signature is of any other type, it throws. If an s-type - member's address is 0, `null` is returned. - */ - const __memberToJsString = function f(obj, memberName) { - const m = __lookupMember(obj.structInfo, memberName, true); - __affirmCStringSignature(m); - const addr = obj[m.key]; - if (!addr) return null; - let pos = addr; - const mem = heap(); - for (; mem[pos] !== 0; ++pos); - return addr === pos ? "" : __utf8Decode(mem, addr, pos); - }; - /** - Adds value v to obj.ondispose, creating ondispose, - or converting it to an array, if needed. - */ - const __addOnDispose = function(obj, ...v) { - if (obj.ondispose) { - if (!Array.isArray(obj.ondispose)) obj.ondispose = [obj.ondispose]; - } else obj.ondispose = []; - obj.ondispose.push(...v); - }; - /** - Allocates a new UTF-8-encoded, NUL-terminated copy of the given - JS string and returns its address relative to heap(). If - allocation returns 0 this function throws. Ownership of the - memory is transfered to the caller, who must eventually pass it - to the configured dealloc() function. - */ - const __allocCString = function(str) { - const u = __utf8Encoder.encode(str); - const mem = alloc(u.length + 1); - if (!mem) toss("Allocation error while duplicating string:", str); - const h = heap(); - h.set(u, Number(mem)); - h[__ptrAdd(mem, u.length)] = 0; - return mem; - }; - /** - Sets the given struct member of obj to a dynamically-allocated, - UTF-8-encoded, NUL-terminated copy of str. It is up to the caller - to free any prior memory, if appropriate. The newly-allocated - string is added to obj.ondispose so will be freed when the object - is disposed. - - The given name may be either the name of the structInfo.members - key (faster) or the key as modified by the memberPrefix and - memberSuffix settings. - */ - const __setMemberCString = function(obj, memberName, str) { - const m = __lookupMember(obj.structInfo, memberName, true); - __affirmCStringSignature(m); - const mem = __allocCString(str); - obj[m.key] = mem; - __addOnDispose(obj, mem); - return obj; - }; - /** - Prototype for all StructFactory instances (the constructors - returned from StructBinder). - */ - const StructType = function StructType(structName, structInfo) { - if (arguments[2] !== rop) toss("Do not call the StructType constructor", "from client-level code."); - Object.defineProperties(this, { - structName: rop(structName), - structInfo: rop(structInfo) - }); - }; - /** - Properties inherited by struct-type-specific StructType instances - and (indirectly) concrete struct-type instances. - */ - StructType.prototype = Object.create(null, { - dispose: rop(function() { - __freeStruct(this.constructor, this); - }), - lookupMember: rop(function(memberName, tossIfNotFound = true) { - return __lookupMember(this.structInfo, memberName, tossIfNotFound); - }), - memberToJsString: rop(function(memberName) { - return __memberToJsString(this, memberName); - }), - memberIsString: rop(function(memberName, tossIfNotFound = true) { - return __memberIsString(this, memberName, tossIfNotFound); - }), - memberKey: __memberKeyProp, - memberKeys: __structMemberKeys, - memberSignature: rop(function(memberName, emscriptenFormat = false) { - return __memberSignature(this, memberName, emscriptenFormat); - }), - memoryDump: rop(function() { - const p = this.pointer; - return p ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof)) : null; - }), - extraBytes: { - configurable: false, - enumerable: false, - get: function() { - return getInstanceHandle(this, false)?.xb ?? 0; - } - }, - zeroOnDispose: { - configurable: false, - enumerable: false, - get: function() { - return getInstanceHandle(this, false)?.zod ?? !!this.structInfo.zeroOnDispose; - } - }, - pointer: { - configurable: false, - enumerable: false, - get: function() { - return getInstanceHandle(this, false)?.p; - }, - set: () => toss("Cannot assign the 'pointer' property of a struct.") - }, - setMemberCString: rop(function(memberName, str) { - return __setMemberCString(this, memberName, str); - }) - }); - Object.assign(StructType.prototype, { addOnDispose: function(...v) { - __addOnDispose(this, ...v); - return this; - } }); - /** - "Static" properties for StructType. - */ - Object.defineProperties(StructType, { - allocCString: rop(__allocCString), - isA: rop((v) => v instanceof StructType), - hasExternalPointer: rop((v) => { - const ii = getInstanceHandle(v, false); - return !!(ii?.p && !ii?.ownsPointer); - }), - memberKey: __memberKeyProp - }); - /** - If struct description object si has a getter proxy, return it (a - function), else return undefined. - */ - const memberGetterProxy = function(si) { - return si.get || (si.adaptGet ? StructBinder.adaptGet(si.adaptGet) : void 0); - }; - /** - If struct description object si has a setter proxy, return it (a - function), else return undefined. - */ - const memberSetterProxy = function(si) { - return si.set || (si.adaptSet ? StructBinder.adaptSet(si.adaptSet) : void 0); - }; - /** - To be called by makeMemberWrapper() when si has a 'members' - member, i.e. is an embedded struct. This function sets up that - struct like any other and also sets up property accessor for - ctor.memberKey(name) which returns an instance of that new - StructType when the member is accessed. That instance wraps the - memory of the member's part of the containing C struct instance. - - That is, if struct Foo has member bar which is an inner struct - then: - - const f = new Foo; - const b = f.bar; - assert( b is-a StructType object ); - assert( b.pointer === f.b.pointer ); - - b will be disposed of when f() is. Calling b.dispose() will not - do any permanent harm, as the wrapper object will be recreated - when accessing f.bar, pointing to the same memory in f. - - The si.zeroOnDispose flag has no effect on embedded structs because - they wrap "external" memory, so do not own it, and are thus never - freed, as such. - */ - const makeMemberStructWrapper = function callee(ctor, name, si) { - /** - Where we store inner-struct member proxies. Keys are a - combination of the parent object's pointer address and the - property's name. The values are StructType instances. - */ - const __innerStructs = callee.innerStructs ??= /* @__PURE__ */ new Map(); - const key = ctor.memberKey(name); - if (void 0 !== si.signature) toss("'signature' cannot be used on an embedded struct (", ctor.structName, ".", key, ")."); - if (memberSetterProxy(si)) toss("'set' and 'adaptSet' are not permitted for nested struct members."); - si.structName ??= ctor.structName + "::" + name; - si.key = key; - si.name = name; - si.constructor = this.call(this, si.structName, si); - const getterProxy = memberGetterProxy(si); - const prop = Object.assign(Object.create(null), { - configurable: false, - enumerable: false, - set: __propThrowOnSet(ctor.structName, key), - get: function() { - const dbg = this.debugFlags.__flags; - const p = this.pointer; - const k = p + "." + key; - let s = __innerStructs.get(k); - if (dbg.getter) log("debug.getter: k =", k); - if (!s) { - s = new si.constructor(__ptrAdd(p, si.offset)); - __innerStructs.set(k, s); - this.addOnDispose(() => s.dispose()); - s.addOnDispose(() => __innerStructs.delete(k)); - } - if (getterProxy) s = getterProxy.apply(this, [s, key]); - if (dbg.getter) log("debug.getter: result =", s); - return s; - } - }); - Object.defineProperty(ctor.prototype, key, prop); - }; - /** - This is where most of the magic happens. - - Pass this a StructBinderImpl-generated constructor, a member - property name, and the struct member description object. It will - define property accessors for proto[memberKey] which read - from/write to memory in this.pointer. It modifies si to make - certain downstream operations simpler. - */ - const makeMemberWrapper = function f(ctor, name, si) { - si = __adaptStruct2(this, si); - if (si.members) return makeMemberStructWrapper.call(this, ctor, name, si); - if (!f.cache) { - f.cache = { - getters: {}, - setters: {}, - sw: {} - }; - const a = [ - "i", - "c", - "C", - "p", - "P", - "s", - "f", - "d", - "v()" - ]; - if (bigIntEnabled) a.push("j"); - a.forEach(function(v) { - f.cache.getters[v] = sigDVGetter(v); - f.cache.setters[v] = sigDVSetter(v); - f.cache.sw[v] = sigDVSetWrapper(v); - }); - f.sigCheck = function(obj, name, key, sig) { - if (Object.prototype.hasOwnProperty.call(obj, key)) toss(obj.structName, "already has a property named", key + "."); - looksLikeASig(sig) || toss("Malformed signature for", sPropName(obj.structName, name) + ":", sig); - }; - } - const key = ctor.memberKey(name); - f.sigCheck(ctor.prototype, name, key, si.signature); - si.key = key; - si.name = name; - const sigGlyph = sigLetter(si.signature); - const xPropName = sPropName(ctor.structName, key); - const dbg = ctor.debugFlags.__flags; - const getterProxy = memberGetterProxy(si); - const prop = Object.create(null); - prop.configurable = false; - prop.enumerable = false; - prop.get = function() { - /** - This getter proxy reads its value from the appropriate pointer - address in the heap. It knows where and how much to read based on - this.pointer, si.offset, and si.sizeof. - */ - if (dbg.getter) log("debug.getter:", f.cache.getters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof); - let rc = new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.getters[sigGlyph]](0, isLittleEndian); - if (getterProxy) rc = getterProxy.apply(this, [key, rc]); - if (dbg.getter) log("debug.getter:", xPropName, "result =", rc); - return rc; - }; - if (si.readOnly) prop.set = __propThrowOnSet(ctor.prototype.structName, key); - else { - const setterProxy = memberSetterProxy(si); - prop.set = function(v) { - /** - The converse of prop.get(), this encodes v into the appropriate - spot in the WASM heap. - */ - if (dbg.setter) log("debug.setter:", f.cache.setters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof, v); - if (!this.pointer) toss("Cannot set native property on a disposed", this.structName, "instance."); - if (setterProxy) v = setterProxy.apply(this, [key, v]); - if (null === v || void 0 === v) v = __NullPtr; - else if (isPtrSig(si.signature) && !__isPtr(v)) if (isAutoPtrSig(si.signature) && v instanceof StructType) { - v = v.pointer || __NullPtr; - if (dbg.setter) log("debug.setter:", xPropName, "resolved to", v); - } else toss("Invalid value for pointer-type", xPropName + "."); - new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian); - }; - } - Object.defineProperty(ctor.prototype, key, prop); - }; - /** - The main factory function which will be returned to the - caller. The third argument is structly for internal use. - - This level of indirection is to avoid that clients can pass a - third argument to this, as that's only for internal use. - - internalOpt options: - - - None right now. This is for potential use in recursion. - - Usages: - - StructBinder(string, obj [,optObj]); - StructBinder(obj); - */ - const StructBinderImpl = function StructBinderImpl(structName, si, opt = Object.create(null)) { - /** - StructCtor is the eventual return value of this function. We - need to populate this early on so that we can do some trickery - in feeding it through recursion. - - Uses: - - // heap-allocated: - const x = new StructCtor; - // externally-managed memory: - const y = new StructCtor( aPtrToACompatibleCStruct ); - - or, more recently: - - const z = new StructCtor({ - extraBytes: [int=0] extra bytes to allocate after the struct - - wrap: [aPtrToACompatibleCStruct=undefined]. If provided, this - instance waps, but does not (by default) own the memory, else - a new instance is allocated from the WASM heap. - - ownsPointer: true if this object takes over ownership of - wrap. - - zeroOnDispose: [bool=StructCtor.structInfo.zeroOnDispose] - - autoCalcSizeOffset: [bool=false] Automatically calculate - sizeof an offset. This is fine for pure-JS structs (which - probably aren't useful beyond testing of Jaccwabyt) but it's - dangerous to use with actual WASM objects because we cannot - be guaranteed to have the same memory layout as an ostensibly - matching C struct. This applies recursively to all children - of the struct description. - - // TODO? Per-instance overrides of the struct-level flags? - - get: (k,v)=>v, - set: (k,v)=>v, - adaptGet: string, - adaptSet: string - - // That wouldn't fit really well right now, apparently. - }); - - */ - const StructCtor = function StructCtor(arg) { - if (!(this instanceof StructCtor)) toss("The", structName, "constructor may only be called via 'new'."); - __allocStruct(StructCtor, this, ...arguments); - }; - const self = this; - /** - "Convert" struct description x to a struct description, if - needed. This expands adaptStruct() mappings and transforms - {memberName:signatureString} signature syntax to object form. - */ - const ads = (x) => { - return "string" === typeof x && looksLikeASig(x) ? { signature: x } : __adaptStruct2(self, x); - }; - if (1 === arguments.length) { - si = ads(structName); - structName = si.structName || si.name; - } else if (2 === arguments.length) { - si = ads(si); - si.name ??= structName; - } else si = ads(si); - structName ??= si.structName; - structName ??= opt.structName; - if (!structName) toss("One of 'name' or 'structName' are required."); - if (si.adapt) { - Object.keys(si.adapt.struct || {}).forEach((k) => { - __adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]); - }); - Object.keys(si.adapt.set || {}).forEach((k) => { - __adaptSet.call(StructBinderImpl, k, si.adapt.set[k]); - }); - Object.keys(si.adapt.get || {}).forEach((k) => { - __adaptGet.call(StructBinderImpl, k, si.adapt.get[k]); - }); - } - if (!si.members && !si.sizeof) si.sizeof = sigSize(si.signature); - const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags)); - Object.defineProperties(StructCtor, { - debugFlags, - isA: rop((v) => v instanceof StructCtor), - memberKey: __memberKeyProp, - memberKeys: __structMemberKeys, - structInfo: rop(si), - structName: rop(structName), - ptrAdd: rop(__ptrAdd) - }); - StructCtor.prototype = new StructType(structName, si, rop); - Object.defineProperties(StructCtor.prototype, { - debugFlags, - constructor: rop(StructCtor), - ptrAdd: rop(__ptrAddSelf) - }); - let lastMember = false; - let offset = 0; - const autoCalc = !!si.autoCalcSizeOffset; - if (!autoCalc) { - if (!si.sizeof) toss(structName, "description is missing its sizeof property."); - si.offset ??= 0; - } else si.offset ??= 0; - Object.keys(si.members || {}).forEach((k) => { - let m = ads(si.members[k]); - if (!m.members && !m.sizeof) { - m.sizeof = sigSize(m.signature); - if (!m.sizeof) toss(sPropName(structName, k), "is missing a sizeof property.", m); - } - if (void 0 === m.offset) if (autoCalc) m.offset = offset; - else toss(sPropName(structName, k), "is missing its offset.", JSON.stringify(m)); - si.members[k] = m; - if (!lastMember || lastMember.offset < m.offset) lastMember = m; - const oldAutoCalc = !!m.autoCalc; - if (autoCalc) m.autoCalcSizeOffset = true; - makeMemberWrapper.call(self, StructCtor, k, m); - if (oldAutoCalc) m.autoCalcSizeOffset = true; - else delete m.autoCalcSizeOffset; - offset += m.sizeof; - }); - if (!lastMember) toss("No member property descriptions found."); - if (!si.sizeof) si.sizeof = offset; - if (si.sizeof === 1) si.signature === "c" || si.signature === "C" || toss("Unexpected sizeof==1 member", sPropName(structName, k), "with signature", si.signature); - else { - if (0 !== si.sizeof % 4) { - console.warn("Invalid struct member description", si); - toss(structName, "sizeof is not aligned. sizeof=" + si.sizeof); - } - if (0 !== si.offset % 4) { - console.warn("Invalid struct member description", si); - toss(structName, "offset is not aligned. offset=" + si.offset); - } - } - if (si.sizeof < offset) { - console.warn("Suspect struct description:", si, "offset =", offset); - toss("Mismatch in the calculated vs. the provided sizeof/offset info.", "Expected sizeof", offset, "but got", si.sizeof, "for", si); - } - delete si.autoCalcSizeOffset; - return StructCtor; - }; - const StructBinder = function StructBinder(structName, structInfo) { - return 1 == arguments.length ? StructBinderImpl.call(StructBinder, structName) : StructBinderImpl.call(StructBinder, structName, structInfo); - }; - StructBinder.StructType = StructType; - StructBinder.config = config; - StructBinder.allocCString = __allocCString; - StructBinder.adaptGet = __adaptGet; - StructBinder.adaptSet = __adaptSet; - StructBinder.adaptStruct = __adaptStruct; - StructBinder.ptrAdd = __ptrAdd; - if (!StructBinder.debugFlags) StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags); - return StructBinder; - }; - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - "use strict"; - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; - globalThis.WhWasmUtilInstaller(wasm); - delete globalThis.WhWasmUtilInstaller; - /** - Signatures for the WASM-exported C-side functions. Each entry - is an array with 2+ elements: - - [ "c-side name", - "result type" (wasm.xWrap() syntax), - [arg types in xWrap() syntax] - // ^^^ this needn't strictly be an array: it can be subsequent - // elements instead: [x,y,z] is equivalent to x,y,z - ] - - Support for the API-specific data types in the result/argument - type strings gets plugged in at a later phase in the API - initialization process. - */ - const bindingSignatures = { - core: [ - [ - "sqlite3_aggregate_context", - "void*", - "sqlite3_context*", - "int" - ], - [ - "sqlite3_bind_double", - "int", - "sqlite3_stmt*", - "int", - "f64" - ], - [ - "sqlite3_bind_int", - "int", - "sqlite3_stmt*", - "int", - "int" - ], - [ - "sqlite3_bind_null", - void 0, - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_bind_parameter_count", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_bind_parameter_index", - "int", - "sqlite3_stmt*", - "string" - ], - [ - "sqlite3_bind_parameter_name", - "string", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_bind_pointer", - "int", - "sqlite3_stmt*", - "int", - "*", - "string:static", - "*" - ], - [ - "sqlite3_busy_handler", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - signature: "i(pi)", - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_busy_timeout", - "int", - "sqlite3*", - "int" - ], - [ - "sqlite3_changes", - "int", - "sqlite3*" - ], - [ - "sqlite3_clear_bindings", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_collation_needed", - "int", - "sqlite3*", - "*", - "*" - ], - [ - "sqlite3_column_blob", - "*", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_bytes", - "int", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_count", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_column_decltype", - "string", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_double", - "f64", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_int", - "int", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_name", - "string", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_type", - "int", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_column_value", - "sqlite3_value*", - "sqlite3_stmt*", - "int" - ], - [ - "sqlite3_commit_hook", - "void*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_commit_hook", - signature: "i(p)", - contextKey: (argv) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_compileoption_get", - "string", - "int" - ], - [ - "sqlite3_compileoption_used", - "int", - "string" - ], - [ - "sqlite3_complete", - "int", - "string:flexible" - ], - [ - "sqlite3_context_db_handle", - "sqlite3*", - "sqlite3_context*" - ], - [ - "sqlite3_data_count", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_db_filename", - "string", - "sqlite3*", - "string" - ], - [ - "sqlite3_db_handle", - "sqlite3*", - "sqlite3_stmt*" - ], - [ - "sqlite3_db_name", - "string", - "sqlite3*", - "int" - ], - [ - "sqlite3_db_readonly", - "int", - "sqlite3*", - "string" - ], - [ - "sqlite3_db_status", - "int", - "sqlite3*", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_errcode", - "int", - "sqlite3*" - ], - [ - "sqlite3_errmsg", - "string", - "sqlite3*" - ], - [ - "sqlite3_error_offset", - "int", - "sqlite3*" - ], - [ - "sqlite3_errstr", - "string", - "int" - ], - [ - "sqlite3_exec", - "int", - [ - "sqlite3*", - "string:flexible", - new wasm.xWrap.FuncPtrAdapter({ - signature: "i(pipp)", - bindScope: "transient", - callProxy: (callback) => { - let aNames; - return (pVoid, nCols, pColVals, pColNames) => { - try { - const aVals = wasm.cArgvToJs(nCols, pColVals); - if (!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); - return callback(aVals, aNames) | 0; - } catch (e) { - return e.resultCode || capi.SQLITE_ERROR; - } - }; - } - }), - "*", - "**" - ] - ], - [ - "sqlite3_expanded_sql", - "string", - "sqlite3_stmt*" - ], - [ - "sqlite3_extended_errcode", - "int", - "sqlite3*" - ], - [ - "sqlite3_extended_result_codes", - "int", - "sqlite3*", - "int" - ], - [ - "sqlite3_file_control", - "int", - "sqlite3*", - "string", - "int", - "*" - ], - [ - "sqlite3_finalize", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_free", - void 0, - "*" - ], - [ - "sqlite3_get_autocommit", - "int", - "sqlite3*" - ], - [ - "sqlite3_get_auxdata", - "*", - "sqlite3_context*", - "int" - ], - ["sqlite3_initialize", void 0], - [ - "sqlite3_interrupt", - void 0, - "sqlite3*" - ], - [ - "sqlite3_is_interrupted", - "int", - "sqlite3*" - ], - ["sqlite3_keyword_count", "int"], - [ - "sqlite3_keyword_name", - "int", - [ - "int", - "**", - "*" - ] - ], - [ - "sqlite3_keyword_check", - "int", - ["string", "int"] - ], - ["sqlite3_libversion", "string"], - ["sqlite3_libversion_number", "int"], - [ - "sqlite3_limit", - "int", - [ - "sqlite3*", - "int", - "int" - ] - ], - [ - "sqlite3_malloc", - "*", - "int" - ], - [ - "sqlite3_next_stmt", - "sqlite3_stmt*", - ["sqlite3*", "sqlite3_stmt*"] - ], - [ - "sqlite3_open", - "int", - "string", - "*" - ], - [ - "sqlite3_open_v2", - "int", - "string", - "*", - "int", - "string" - ], - [ - "sqlite3_realloc", - "*", - "*", - "int" - ], - [ - "sqlite3_reset", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_result_blob", - void 0, - "sqlite3_context*", - "*", - "int", - "*" - ], - [ - "sqlite3_result_double", - void 0, - "sqlite3_context*", - "f64" - ], - [ - "sqlite3_result_error", - void 0, - "sqlite3_context*", - "string", - "int" - ], - [ - "sqlite3_result_error_code", - void 0, - "sqlite3_context*", - "int" - ], - [ - "sqlite3_result_error_nomem", - void 0, - "sqlite3_context*" - ], - [ - "sqlite3_result_error_toobig", - void 0, - "sqlite3_context*" - ], - [ - "sqlite3_result_int", - void 0, - "sqlite3_context*", - "int" - ], - [ - "sqlite3_result_null", - void 0, - "sqlite3_context*" - ], - [ - "sqlite3_result_pointer", - void 0, - "sqlite3_context*", - "*", - "string:static", - "*" - ], - [ - "sqlite3_result_subtype", - void 0, - "sqlite3_value*", - "int" - ], - [ - "sqlite3_result_text", - void 0, - "sqlite3_context*", - "string", - "int", - "*" - ], - [ - "sqlite3_result_zeroblob", - void 0, - "sqlite3_context*", - "int" - ], - [ - "sqlite3_rollback_hook", - "void*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_rollback_hook", - signature: "v(p)", - contextKey: (argv) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_set_auxdata", - void 0, - [ - "sqlite3_context*", - "int", - "*", - "*" - ] - ], - [ - "sqlite3_set_errmsg", - "int", - "sqlite3*", - "int", - "string" - ], - ["sqlite3_shutdown", void 0], - ["sqlite3_sourceid", "string"], - [ - "sqlite3_sql", - "string", - "sqlite3_stmt*" - ], - [ - "sqlite3_status", - "int", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_step", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_stmt_busy", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_stmt_readonly", - "int", - "sqlite3_stmt*" - ], - [ - "sqlite3_stmt_status", - "int", - "sqlite3_stmt*", - "int", - "int" - ], - [ - "sqlite3_strglob", - "int", - "string", - "string" - ], - [ - "sqlite3_stricmp", - "int", - "string", - "string" - ], - [ - "sqlite3_strlike", - "int", - "string", - "string", - "int" - ], - [ - "sqlite3_strnicmp", - "int", - "string", - "string", - "int" - ], - [ - "sqlite3_table_column_metadata", - "int", - "sqlite3*", - "string", - "string", - "string", - "**", - "**", - "*", - "*", - "*" - ], - [ - "sqlite3_total_changes", - "int", - "sqlite3*" - ], - [ - "sqlite3_trace_v2", - "int", - [ - "sqlite3*", - "int", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_trace_v2::callback", - signature: "i(ippp)", - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ], - [ - "sqlite3_txn_state", - "int", - ["sqlite3*", "string"] - ], - [ - "sqlite3_uri_boolean", - "int", - "sqlite3_filename", - "string", - "int" - ], - [ - "sqlite3_uri_key", - "string", - "sqlite3_filename", - "int" - ], - [ - "sqlite3_uri_parameter", - "string", - "sqlite3_filename", - "string" - ], - [ - "sqlite3_user_data", - "void*", - "sqlite3_context*" - ], - [ - "sqlite3_value_blob", - "*", - "sqlite3_value*" - ], - [ - "sqlite3_value_bytes", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_double", - "f64", - "sqlite3_value*" - ], - [ - "sqlite3_value_dup", - "sqlite3_value*", - "sqlite3_value*" - ], - [ - "sqlite3_value_free", - void 0, - "sqlite3_value*" - ], - [ - "sqlite3_value_frombind", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_int", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_nochange", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_numeric_type", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_pointer", - "*", - "sqlite3_value*", - "string:static" - ], - [ - "sqlite3_value_subtype", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_value_type", - "int", - "sqlite3_value*" - ], - [ - "sqlite3_vfs_find", - "*", - "string" - ], - [ - "sqlite3_vfs_register", - "int", - "sqlite3_vfs*", - "int" - ], - [ - "sqlite3_vfs_unregister", - "int", - "sqlite3_vfs*" - ] - ], - int64: [ - [ - "sqlite3_bind_int64", - "int", - [ - "sqlite3_stmt*", - "int", - "i64" - ] - ], - [ - "sqlite3_changes64", - "i64", - ["sqlite3*"] - ], - [ - "sqlite3_column_int64", - "i64", - ["sqlite3_stmt*", "int"] - ], - [ - "sqlite3_deserialize", - "int", - "sqlite3*", - "string", - "*", - "i64", - "i64", - "int" - ], - [ - "sqlite3_last_insert_rowid", - "i64", - ["sqlite3*"] - ], - [ - "sqlite3_malloc64", - "*", - "i64" - ], - [ - "sqlite3_msize", - "i64", - "*" - ], - [ - "sqlite3_overload_function", - "int", - [ - "sqlite3*", - "string", - "int" - ] - ], - [ - "sqlite3_realloc64", - "*", - "*", - "i64" - ], - [ - "sqlite3_result_int64", - void 0, - "*", - "i64" - ], - [ - "sqlite3_result_zeroblob64", - "int", - "*", - "i64" - ], - [ - "sqlite3_serialize", - "*", - "sqlite3*", - "string", - "*", - "int" - ], - [ - "sqlite3_set_last_insert_rowid", - void 0, - ["sqlite3*", "i64"] - ], - [ - "sqlite3_status64", - "int", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_db_status64", - "int", - "sqlite3*", - "int", - "*", - "*", - "int" - ], - [ - "sqlite3_total_changes64", - "i64", - ["sqlite3*"] - ], - [ - "sqlite3_update_hook", - "*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_update_hook::callback", - signature: "v(pippj)", - contextKey: (argv) => argv[0], - callProxy: (callback) => { - return (p, op, z0, z1, rowid) => { - callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid); - }; - } - }), - "*" - ] - ], - [ - "sqlite3_uri_int64", - "i64", - [ - "sqlite3_filename", - "string", - "i64" - ] - ], - [ - "sqlite3_value_int64", - "i64", - "sqlite3_value*" - ] - ], - wasmInternal: [ - [ - "sqlite3__wasm_db_reset", - "int", - "sqlite3*" - ], - [ - "sqlite3__wasm_db_vfs", - "sqlite3_vfs*", - "sqlite3*", - "string" - ], - [ - "sqlite3__wasm_vfs_create_file", - "int", - "sqlite3_vfs*", - "string", - "*", - "int" - ], - [ - "sqlite3__wasm_posix_create_file", - "int", - "string", - "*", - "int" - ], - [ - "sqlite3__wasm_vfs_unlink", - "int", - "sqlite3_vfs*", - "string" - ], - [ - "sqlite3__wasm_qfmt_token", - "string:dealloc", - "string", - "int" - ] - ] - }; - if (!!wasm.exports.sqlite3_progress_handler) bindingSignatures.core.push([ - "sqlite3_progress_handler", - void 0, - [ - "sqlite3*", - "int", - new wasm.xWrap.FuncPtrAdapter({ - name: "xProgressHandler", - signature: "i(p)", - bindScope: "context", - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ]); - if (!!wasm.exports.sqlite3_stmt_explain) bindingSignatures.core.push([ - "sqlite3_stmt_explain", - "int", - "sqlite3_stmt*", - "int" - ], [ - "sqlite3_stmt_isexplain", - "int", - "sqlite3_stmt*" - ]); - if (!!wasm.exports.sqlite3_set_authorizer) bindingSignatures.core.push([ - "sqlite3_set_authorizer", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_set_authorizer::xAuth", - signature: "i(pissss)", - contextKey: (argv, argIndex) => argv[0], - callProxy: (callback) => { - return (pV, iCode, s0, s1, s2, s3) => { - try { - s0 = s0 && wasm.cstrToJs(s0); - s1 = s1 && wasm.cstrToJs(s1); - s2 = s2 && wasm.cstrToJs(s2); - s3 = s3 && wasm.cstrToJs(s3); - return callback(pV, iCode, s0, s1, s2, s3) | 0; - } catch (e) { - return e.resultCode || capi.SQLITE_ERROR; - } - }; - } - }), - "*" - ] - ]); - if (!!wasm.exports.sqlite3_column_origin_name) bindingSignatures.core.push([ - "sqlite3_column_database_name", - "string", - "sqlite3_stmt*", - "int" - ], [ - "sqlite3_column_origin_name", - "string", - "sqlite3_stmt*", - "int" - ], [ - "sqlite3_column_table_name", - "string", - "sqlite3_stmt*", - "int" - ]); - if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_declare_vtab) bindingSignatures.int64.push([ - "sqlite3_create_module", - "int", - [ - "sqlite3*", - "string", - "sqlite3_module*", - "*" - ] - ], [ - "sqlite3_create_module_v2", - "int", - [ - "sqlite3*", - "string", - "sqlite3_module*", - "*", - "*" - ] - ], [ - "sqlite3_declare_vtab", - "int", - ["sqlite3*", "string:flexible"] - ], [ - "sqlite3_drop_modules", - "int", - ["sqlite3*", "**"] - ], [ - "sqlite3_vtab_collation", - "string", - "sqlite3_index_info*", - "int" - ], [ - "sqlite3_vtab_distinct", - "int", - "sqlite3_index_info*" - ], [ - "sqlite3_vtab_in", - "int", - "sqlite3_index_info*", - "int", - "int" - ], [ - "sqlite3_vtab_in_first", - "int", - "sqlite3_value*", - "**" - ], [ - "sqlite3_vtab_in_next", - "int", - "sqlite3_value*", - "**" - ], [ - "sqlite3_vtab_nochange", - "int", - "sqlite3_context*" - ], [ - "sqlite3_vtab_on_conflict", - "int", - "sqlite3*" - ], [ - "sqlite3_vtab_rhs_value", - "int", - "sqlite3_index_info*", - "int", - "**" - ]); - if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_preupdate_hook) bindingSignatures.int64.push([ - "sqlite3_preupdate_blobwrite", - "int", - "sqlite3*" - ], [ - "sqlite3_preupdate_count", - "int", - "sqlite3*" - ], [ - "sqlite3_preupdate_depth", - "int", - "sqlite3*" - ], [ - "sqlite3_preupdate_hook", - "*", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_preupdate_hook", - signature: "v(ppippjj)", - contextKey: (argv) => argv[0], - callProxy: (callback) => { - return (p, db, op, zDb, zTbl, iKey1, iKey2) => { - callback(p, db, op, wasm.cstrToJs(zDb), wasm.cstrToJs(zTbl), iKey1, iKey2); - }; - } - }), - "*" - ] - ], [ - "sqlite3_preupdate_new", - "int", - [ - "sqlite3*", - "int", - "**" - ] - ], [ - "sqlite3_preupdate_old", - "int", - [ - "sqlite3*", - "int", - "**" - ] - ]); - if (wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add && !!wasm.exports.sqlite3session_create && !!wasm.exports.sqlite3_preupdate_hook) { - /** - FuncPtrAdapter options for session-related callbacks with the - native signature "i(ps)". This proxy converts the 2nd argument - from a C string to a JS string before passing the arguments on - to the client-provided JS callback. - */ - const __ipsProxy = { - signature: "i(ps)", - callProxy: (callback) => { - return (p, s) => { - try { - return callback(p, wasm.cstrToJs(s)) | 0; - } catch (e) { - return e.resultCode || capi.SQLITE_ERROR; - } - }; - } - }; - bindingSignatures.int64.push([ - "sqlite3changegroup_add", - "int", - [ - "sqlite3_changegroup*", - "int", - "void*" - ] - ], [ - "sqlite3changegroup_add_strm", - "int", - [ - "sqlite3_changegroup*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changegroup_delete", - void 0, - ["sqlite3_changegroup*"] - ], [ - "sqlite3changegroup_new", - "int", - ["**"] - ], [ - "sqlite3changegroup_output", - "int", - [ - "sqlite3_changegroup*", - "int*", - "**" - ] - ], [ - "sqlite3changegroup_output_strm", - "int", - [ - "sqlite3_changegroup*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppi)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_apply", - "int", - [ - "sqlite3*", - "int", - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_apply_strm", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_apply_v2", - "int", - [ - "sqlite3*", - "int", - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_apply_v2_strm", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - bindScope: "transient", - ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_apply_v3", - "int", - [ - "sqlite3*", - "int", - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - signature: "i(pp)", - bindScope: "transient" - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_apply_v3_strm", - "int", - [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - signature: "i(pp)", - bindScope: "transient" - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xConflict", - signature: "i(pip)", - bindScope: "transient" - }), - "void*", - "**", - "int*", - "int" - ] - ], [ - "sqlite3changeset_concat", - "int", - [ - "int", - "void*", - "int", - "void*", - "int*", - "**" - ] - ], [ - "sqlite3changeset_concat_strm", - "int", - [ - new wasm.xWrap.FuncPtrAdapter({ - name: "xInputA", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInputB", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppi)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_conflict", - "int", - [ - "sqlite3_changeset_iter*", - "int", - "**" - ] - ], [ - "sqlite3changeset_finalize", - "int", - ["sqlite3_changeset_iter*"] - ], [ - "sqlite3changeset_fk_conflicts", - "int", - ["sqlite3_changeset_iter*", "int*"] - ], [ - "sqlite3changeset_invert", - "int", - [ - "int", - "void*", - "int*", - "**" - ] - ], [ - "sqlite3changeset_invert_strm", - "int", - [ - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppi)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_new", - "int", - [ - "sqlite3_changeset_iter*", - "int", - "**" - ] - ], [ - "sqlite3changeset_next", - "int", - ["sqlite3_changeset_iter*"] - ], [ - "sqlite3changeset_old", - "int", - [ - "sqlite3_changeset_iter*", - "int", - "**" - ] - ], [ - "sqlite3changeset_op", - "int", - [ - "sqlite3_changeset_iter*", - "**", - "int*", - "int*", - "int*" - ] - ], [ - "sqlite3changeset_pk", - "int", - [ - "sqlite3_changeset_iter*", - "**", - "int*" - ] - ], [ - "sqlite3changeset_start", - "int", - [ - "**", - "int", - "*" - ] - ], [ - "sqlite3changeset_start_strm", - "int", - [ - "**", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3changeset_start_v2", - "int", - [ - "**", - "int", - "*", - "int" - ] - ], [ - "sqlite3changeset_start_v2_strm", - "int", - [ - "**", - new wasm.xWrap.FuncPtrAdapter({ - name: "xInput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*", - "int" - ] - ], [ - "sqlite3session_attach", - "int", - ["sqlite3_session*", "string"] - ], [ - "sqlite3session_changeset", - "int", - [ - "sqlite3_session*", - "int*", - "**" - ] - ], [ - "sqlite3session_changeset_size", - "i64", - ["sqlite3_session*"] - ], [ - "sqlite3session_changeset_strm", - "int", - [ - "sqlite3_session*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3session_config", - "int", - ["int", "void*"] - ], [ - "sqlite3session_create", - "int", - [ - "sqlite3*", - "string", - "**" - ] - ], [ - "sqlite3session_diff", - "int", - [ - "sqlite3_session*", - "string", - "string", - "**" - ] - ], [ - "sqlite3session_enable", - "int", - ["sqlite3_session*", "int"] - ], [ - "sqlite3session_indirect", - "int", - ["sqlite3_session*", "int"] - ], [ - "sqlite3session_isempty", - "int", - ["sqlite3_session*"] - ], [ - "sqlite3session_memory_used", - "i64", - ["sqlite3_session*"] - ], [ - "sqlite3session_object_config", - "int", - [ - "sqlite3_session*", - "int", - "void*" - ] - ], [ - "sqlite3session_patchset", - "int", - [ - "sqlite3_session*", - "*", - "**" - ] - ], [ - "sqlite3session_patchset_strm", - "int", - [ - "sqlite3_session*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xOutput", - signature: "i(ppp)", - bindScope: "transient" - }), - "void*" - ] - ], [ - "sqlite3session_table_filter", - void 0, - [ - "sqlite3_session*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFilter", - ...__ipsProxy, - contextKey: (argv, argIndex) => argv[0] - }), - "*" - ] - ]); - } - /** - Prepare JS<->C struct bindings for the non-opaque struct types we - need... - */ - sqlite3.StructBinder = globalThis.Jaccwabyt({ - heap: wasm.heap8u, - alloc: wasm.alloc, - dealloc: wasm.dealloc, - bigIntEnabled: wasm.bigIntEnabled, - pointerIR: wasm.ptr.ir, - memberPrefix: "$" - }); - delete globalThis.Jaccwabyt; - { - const __xString = wasm.xWrap.argAdapter("string"); - wasm.xWrap.argAdapter("string:flexible", (v) => __xString(util.flexibleString(v))); - /** - The 'string:static' argument adapter treats its argument as - either... - - - WASM pointer: assumed to be a long-lived C-string which gets - returned as-is. - - - Anything else: gets coerced to a JS string for use as a map - key. If a matching entry is found (as described next), it is - returned, else wasm.allocCString() is used to create a a new - string, map its pointer to a copy of (''+v) for the remainder - of the application's life, and returns that pointer value for - this call and all future calls which are passed a - string-equivalent argument. - - Use case: sqlite3_bind_pointer(), sqlite3_result_pointer(), and - sqlite3_value_pointer() call for "a static string and - preferably a string literal". This converter is used to ensure - that the string value seen by those functions is long-lived and - behaves as they need it to, at the cost of a one-time leak of - each distinct key. - */ - wasm.xWrap.argAdapter("string:static", function(v) { - if (wasm.isPtr(v)) return v; - v = "" + v; - return this[v] || (this[v] = wasm.allocCString(v)); - }.bind(Object.create(null))); - /** - Add some descriptive xWrap() aliases for '*' intended to (A) - improve readability/correctness of bindingSignatures and (B) - provide automatic conversion from higher-level representations, - e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via (capi.sqlite3_vfs - instance).pointer. - */ - const __xArgPtr = wasm.xWrap.argAdapter("*"); - const nilType = function() {}; - wasm.xWrap.argAdapter("sqlite3_filename", __xArgPtr)("sqlite3_context*", __xArgPtr)("sqlite3_value*", __xArgPtr)("void*", __xArgPtr)("sqlite3_changegroup*", __xArgPtr)("sqlite3_changeset_iter*", __xArgPtr)("sqlite3_session*", __xArgPtr)("sqlite3_stmt*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.Stmt || nilType) ? v.pointer : v))("sqlite3*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.DB || nilType) ? v.pointer : v))("sqlite3_vfs*", (v) => { - if ("string" === typeof v) return capi.sqlite3_vfs_find(v) || sqlite3.SQLite3Error.toss(capi.SQLITE_NOTFOUND, "Unknown sqlite3_vfs name:", v); - return __xArgPtr(v instanceof (capi.sqlite3_vfs || nilType) ? v.pointer : v); - }); - if (wasm.exports.sqlite3_declare_vtab) wasm.xWrap.argAdapter("sqlite3_index_info*", (v) => __xArgPtr(v instanceof (capi.sqlite3_index_info || nilType) ? v.pointer : v))("sqlite3_module*", (v) => __xArgPtr(v instanceof (capi.sqlite3_module || nilType) ? v.pointer : v)); - /** - Alias `T*` to `*` for return type conversions for common T - types, primarily to improve legibility of their binding - signatures. - */ - const __xRcPtr = wasm.xWrap.resultAdapter("*"); - wasm.xWrap.resultAdapter("sqlite3*", __xRcPtr)("sqlite3_context*", __xRcPtr)("sqlite3_stmt*", __xRcPtr)("sqlite3_value*", __xRcPtr)("sqlite3_vfs*", __xRcPtr)("void*", __xRcPtr); - /** - Populate api object with sqlite3_...() by binding the "raw" wasm - exports into type-converting proxies using wasm.xWrap(). - */ - for (const e of bindingSignatures.core) capi[e[0]] = wasm.xWrap.apply(null, e); - for (const e of bindingSignatures.wasmInternal) util[e[0]] = wasm.xWrap.apply(null, e); - for (const e of bindingSignatures.int64) capi[e[0]] = wasm.bigIntEnabled ? wasm.xWrap.apply(null, e) : () => toss(e[0] + "() is unavailable due to lack", "of BigInt support in this build."); - delete bindingSignatures.core; - delete bindingSignatures.int64; - delete bindingSignatures.wasmInternal; - /** - Sets the given db's error state. Accepts: - - - (sqlite3*, int code, string msg) - - (sqlite3*, Error e [,string msg = ''+e]) - - If passed a WasmAllocError, the message is ignored and the - result code is SQLITE_NOMEM. If passed any other Error type, - the result code defaults to SQLITE_ERROR unless the Error - object has a resultCode property, in which case that is used - (e.g. SQLite3Error has that). If passed a non-WasmAllocError - exception, the message string defaults to ''+theError. - - Returns either the final result code, capi.SQLITE_NOMEM if - setting the message string triggers an OOM, or - capi.SQLITE_MISUSE if pDb is NULL or invalid (with the caveat - that behavior in the later case is undefined if pDb is not - "valid enough"). - - Pass (pDb,0,0) to clear the error state. - */ - util.sqlite3__wasm_db_error = function(pDb, resultCode, message) { - if (!pDb) return capi.SQLITE_MISUSE; - if (resultCode instanceof sqlite3.WasmAllocError) { - resultCode = capi.SQLITE_NOMEM; - message = 0; - } else if (resultCode instanceof Error) { - message = message || "" + resultCode; - resultCode = resultCode.resultCode || capi.SQLITE_ERROR; - } - return capi.sqlite3_set_errmsg(pDb, resultCode, message) || resultCode; - }; - } - { - const cJson = wasm.xCall("sqlite3__wasm_enum_json"); - if (!cJson) toss("Maintenance required: increase sqlite3__wasm_enum_json()'s", "static buffer size!"); - wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); - const defineGroups = [ - "access", - "authorizer", - "blobFinalizers", - "changeset", - "config", - "dataTypes", - "dbConfig", - "dbStatus", - "encodings", - "fcntl", - "flock", - "ioCap", - "limits", - "openFlags", - "prepareFlags", - "resultCodes", - "sqlite3Status", - "stmtStatus", - "syncFlags", - "trace", - "txnState", - "udfFlags", - "version" - ]; - if (wasm.bigIntEnabled) defineGroups.push("serialize", "session", "vtab"); - for (const t of defineGroups) for (const e of Object.entries(wasm.ctype[t])) capi[e[0]] = e[1]; - if (!wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)) toss("Internal error: cannot resolve exported function", "entry SQLITE_WASM_DEALLOC (==" + capi.SQLITE_WASM_DEALLOC + ")."); - const __rcMap = Object.create(null); - for (const e of Object.entries(wasm.ctype["resultCodes"])) __rcMap[e[1]] = e[0]; - /** - For the given integer, returns the SQLITE_xxx result code as a - string, or undefined if no such mapping is found. - */ - capi.sqlite3_js_rc_str = (rc) => __rcMap[rc]; - const notThese = Object.assign(Object.create(null), { - WasmTestStruct: true, - sqlite3_index_info: !wasm.bigIntEnabled, - sqlite3_index_constraint: !wasm.bigIntEnabled, - sqlite3_index_orderby: !wasm.bigIntEnabled, - sqlite3_index_constraint_usage: !wasm.bigIntEnabled - }); - for (const s of wasm.ctype.structs) if (!notThese[s.name]) capi[s.name] = sqlite3.StructBinder(s); - if (capi.sqlite3_index_info) { - for (const k of [ - "sqlite3_index_constraint", - "sqlite3_index_orderby", - "sqlite3_index_constraint_usage" - ]) { - capi.sqlite3_index_info[k] = capi[k]; - delete capi[k]; - } - capi.sqlite3_vtab_config = wasm.xWrap("sqlite3__wasm_vtab_config", "int", [ - "sqlite3*", - "int", - "int" - ]); - } - } - /** - Internal helper to assist in validating call argument counts in - the hand-written sqlite3_xyz() wrappers. We do this only for - consistency with non-special-case wrappings. - */ - const __dbArgcMismatch = (pDb, f, n) => { - return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, f + "() requires " + n + " argument" + (1 === n ? "" : "s") + "."); - }; - /** Code duplication reducer for functions which take an encoding - argument and require SQLITE_UTF8. Sets the db error code to - SQLITE_FORMAT, installs a descriptive error message, - and returns SQLITE_FORMAT. */ - const __errEncoding = (pDb) => { - return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding."); - }; - /** - __dbCleanupMap is infrastructure for recording registration of - UDFs and collations so that sqlite3_close_v2() can clean up any - automated JS-to-WASM function conversions installed by those. - */ - const __argPDb = (pDb) => wasm.xWrap.argAdapter("sqlite3*")(pDb); - const __argStr = (str) => wasm.isPtr(str) ? wasm.cstrToJs(str) : str; - const __dbCleanupMap = function(pDb, mode) { - pDb = __argPDb(pDb); - let m = this.dbMap.get(pDb); - if (!mode) { - this.dbMap.delete(pDb); - return m; - } else if (!m && mode > 0) this.dbMap.set(pDb, m = Object.create(null)); - return m; - }.bind(Object.assign(Object.create(null), { dbMap: /* @__PURE__ */ new Map() })); - __dbCleanupMap.addCollation = function(pDb, name) { - const m = __dbCleanupMap(pDb, 1); - if (!m.collation) m.collation = /* @__PURE__ */ new Set(); - m.collation.add(__argStr(name).toLowerCase()); - }; - __dbCleanupMap._addUDF = function(pDb, name, arity, map) { - name = __argStr(name).toLowerCase(); - let u = map.get(name); - if (!u) map.set(name, u = /* @__PURE__ */ new Set()); - u.add(arity < 0 ? -1 : arity); - }; - __dbCleanupMap.addFunction = function(pDb, name, arity) { - const m = __dbCleanupMap(pDb, 1); - if (!m.udf) m.udf = /* @__PURE__ */ new Map(); - this._addUDF(pDb, name, arity, m.udf); - }; - if (wasm.exports.sqlite3_create_window_function) __dbCleanupMap.addWindowFunc = function(pDb, name, arity) { - const m = __dbCleanupMap(pDb, 1); - if (!m.wudf) m.wudf = /* @__PURE__ */ new Map(); - this._addUDF(pDb, name, arity, m.wudf); - }; - /** - Intended to be called _only_ from sqlite3_close_v2(), - passed its non-0 db argument. - - This function frees up certain automatically-installed WASM - function bindings which were installed on behalf of the given db, - as those may otherwise leak. - - Notable caveat: this is only ever run via - sqlite3.capi.sqlite3_close_v2(). If a client, for whatever - reason, uses sqlite3.wasm.exports.sqlite3_close_v2() (the - function directly exported from WASM), this cleanup will not - happen. - - This is not a silver bullet for avoiding automation-related - leaks but represents "an honest effort." - - The issue being addressed here is covered at: - - https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr - */ - __dbCleanupMap.cleanup = function(pDb) { - pDb = __argPDb(pDb); - /** - Installing NULL functions in the C API will remove those - bindings. The FuncPtrAdapter which sits between us and the C - API will also treat that as an opportunity to - wasm.uninstallFunction() any WASM function bindings it has - installed for pDb. - */ - for (const obj of [ - ["sqlite3_busy_handler", 3], - ["sqlite3_commit_hook", 3], - ["sqlite3_preupdate_hook", 3], - ["sqlite3_progress_handler", 4], - ["sqlite3_rollback_hook", 3], - ["sqlite3_set_authorizer", 3], - ["sqlite3_trace_v2", 4], - ["sqlite3_update_hook", 3] - ]) { - const [name, arity] = obj; - if (!wasm.exports[name]) continue; - const closeArgs = [pDb]; - closeArgs.length = arity; - try { - capi[name](...closeArgs); - } catch (e) { - sqlite3.config.warn("close-time call of", name + "(", closeArgs, ") threw:", e); - } - } - const m = __dbCleanupMap(pDb, 0); - if (!m) return; - if (m.collation) { - for (const name of m.collation) try { - capi.sqlite3_create_collation_v2(pDb, name, capi.SQLITE_UTF8, 0, 0, 0); - } catch (e) {} - delete m.collation; - } - let i; - for (i = 0; i < 2; ++i) { - const fmap = i ? m.wudf : m.udf; - if (!fmap) continue; - const func = i ? capi.sqlite3_create_window_function : capi.sqlite3_create_function_v2; - for (const e of fmap) { - const name = e[0], arities = e[1]; - const fargs = [ - pDb, - name, - 0, - capi.SQLITE_UTF8, - 0, - 0, - 0, - 0, - 0 - ]; - if (i) fargs.push(0); - for (const arity of arities) try { - fargs[2] = arity; - func.apply(null, fargs); - } catch (e) {} - arities.clear(); - } - fmap.clear(); - } - delete m.udf; - delete m.wudf; - }; - { - const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*"); - capi.sqlite3_close_v2 = function(pDb) { - if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_close_v2", 1); - if (pDb) try { - __dbCleanupMap.cleanup(pDb); - } catch (e) {} - return __sqlite3CloseV2(pDb); - }; - } - if (capi.sqlite3session_create) { - const __sqlite3SessionDelete = wasm.xWrap("sqlite3session_delete", void 0, ["sqlite3_session*"]); - capi.sqlite3session_delete = function(pSession) { - if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3session_delete", 1); - else if (pSession) capi.sqlite3session_table_filter(pSession, 0, 0); - __sqlite3SessionDelete(pSession); - }; - } - { - const contextKey = (argv, argIndex) => { - return "argv[" + argIndex + "]:" + argv[0] + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); - }; - const __sqlite3CreateCollationV2 = wasm.xWrap("sqlite3_create_collation_v2", "int", [ - "sqlite3*", - "string", - "int", - "*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xCompare", - signature: "i(pipip)", - contextKey - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xDestroy", - signature: "v(p)", - contextKey - }) - ]); - /** - Works exactly like C's sqlite3_create_collation_v2() except that: - - 1) It returns capi.SQLITE_FORMAT if the 3rd argument contains - any encoding-related value other than capi.SQLITE_UTF8. No - other encodings are supported. As a special case, if the - bottom 4 bits of that argument are 0, SQLITE_UTF8 is - assumed. - - 2) It accepts JS functions for its function-pointer arguments, - for which it will install WASM-bound proxies. The bindings - are "permanent," in that they will stay in the WASM - environment until it shuts down unless the client calls this - again with the same collation name and a value of 0 or null - for the the function pointer(s). sqlite3_close_v2() will - also clean up such automatically-installed WASM functions. - - For consistency with the C API, it requires the same number of - arguments. It returns capi.SQLITE_MISUSE if passed any other - argument count. - - Returns 0 on success, non-0 on error, in which case the error - state of pDb (of type `sqlite3*` or argument-convertible to it) - may contain more information. - */ - capi.sqlite3_create_collation_v2 = function(pDb, zName, eTextRep, pArg, xCompare, xDestroy) { - if (6 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_collation_v2", 6); - else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; - else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); - try { - const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); - if (0 === rc && xCompare instanceof Function) __dbCleanupMap.addCollation(pDb, zName); - return rc; - } catch (e) { - return util.sqlite3__wasm_db_error(pDb, e); - } - }; - capi.sqlite3_create_collation = (pDb, zName, eTextRep, pArg, xCompare) => { - return 5 === arguments.length ? capi.sqlite3_create_collation_v2(pDb, zName, eTextRep, pArg, xCompare, 0) : __dbArgcMismatch(pDb, "sqlite3_create_collation", 5); - }; - } - { - /** FuncPtrAdapter for contextKey() for sqlite3_create_function() - and friends. */ - const contextKey = function(argv, argIndex) { - return argv[0] + ":" + (argv[2] < 0 ? -1 : argv[2]) + ":" + argIndex + ":" + wasm.cstrToJs(argv[1]).toLowerCase(); - }; - /** - JS proxies for the various sqlite3_create[_window]_function() - callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter. - */ - const __cfProxy = Object.assign(Object.create(null), { - xInverseAndStep: { - signature: "v(pip)", - contextKey, - callProxy: (callback) => { - return (pCtx, argc, pArgv) => { - try { - callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)); - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xFinalAndValue: { - signature: "v(p)", - contextKey, - callProxy: (callback) => { - return (pCtx) => { - try { - capi.sqlite3_result_js(pCtx, callback(pCtx)); - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xFunc: { - signature: "v(pip)", - contextKey, - callProxy: (callback) => { - return (pCtx, argc, pArgv) => { - try { - capi.sqlite3_result_js(pCtx, callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))); - } catch (e) { - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xDestroy: { - signature: "v(p)", - contextKey, - callProxy: (callback) => { - return (pVoid) => { - try { - callback(pVoid); - } catch (e) { - console.error("UDF xDestroy method threw:", e); - } - }; - } - } - }); - const __sqlite3CreateFunction = wasm.xWrap("sqlite3_create_function_v2", "int", [ - "sqlite3*", - "string", - "int", - "int", - "*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xFunc", - ...__cfProxy.xFunc - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xStep", - ...__cfProxy.xInverseAndStep - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xFinal", - ...__cfProxy.xFinalAndValue - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xDestroy", - ...__cfProxy.xDestroy - }) - ]); - const __sqlite3CreateWindowFunction = wasm.exports.sqlite3_create_window_function ? wasm.xWrap("sqlite3_create_window_function", "int", [ - "sqlite3*", - "string", - "int", - "int", - "*", - new wasm.xWrap.FuncPtrAdapter({ - name: "xStep", - ...__cfProxy.xInverseAndStep - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xFinal", - ...__cfProxy.xFinalAndValue - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xValue", - ...__cfProxy.xFinalAndValue - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xInverse", - ...__cfProxy.xInverseAndStep - }), - new wasm.xWrap.FuncPtrAdapter({ - name: "xDestroy", - ...__cfProxy.xDestroy - }) - ]) : void 0; - capi.sqlite3_create_function_v2 = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_function_v2", f.length); - else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; - else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); - try { - const rc = __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy); - if (0 === rc && (xFunc instanceof Function || xStep instanceof Function || xFinal instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addFunction(pDb, funcName, nArg); - return rc; - } catch (e) { - console.error("sqlite3_create_function_v2() setup threw:", e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); - } - }; - capi.sqlite3_create_function = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) { - return f.length === arguments.length ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, 0) : __dbArgcMismatch(pDb, "sqlite3_create_function", f.length); - }; - if (__sqlite3CreateWindowFunction) capi.sqlite3_create_window_function = function f(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_window_function", f.length); - else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8; - else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb); - try { - const rc = __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy); - if (0 === rc && (xStep instanceof Function || xFinal instanceof Function || xValue instanceof Function || xInverse instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); - return rc; - } catch (e) { - console.error("sqlite3_create_window_function() setup threw:", e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e); - } - }; - else delete capi.sqlite3_create_window_function; - /** - A _deprecated_ alias for capi.sqlite3_result_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfSetResult = capi.sqlite3_create_function.udfSetResult = capi.sqlite3_result_js; - if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js; - /** - A _deprecated_ alias for capi.sqlite3_values_to_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_values_to_js; - if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js; - /** - A _deprecated_ alias for capi.sqlite3_result_error_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfSetError = capi.sqlite3_create_function.udfSetError = capi.sqlite3_result_error_js; - if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js; - } - { - /** - Helper for string:flexible conversions which requires a - byte-length counterpart argument. Passed a value and its - ostensible length, this function returns [V,N], where V is - either v or a to-string transformed copy of v and N is either n - (if v is a WASM pointer, in which case n might be a BigInt), -1 - (if v is a string or Array), or the byte length of v (if it's a - byte array or ArrayBuffer). - */ - const __flexiString = (v, n) => { - if ("string" === typeof v) n = -1; - else if (util.isSQLableTypedArray(v)) { - n = v.byteLength; - v = wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v); - } else if (Array.isArray(v)) { - v = v.join(""); - n = -1; - } - return [v, n]; - }; - /** - Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). - */ - const __prepare = { - basic: wasm.xWrap("sqlite3_prepare_v3", "int", [ - "sqlite3*", - "string", - "int", - "int", - "**", - "**" - ]), - full: wasm.xWrap("sqlite3_prepare_v3", "int", [ - "sqlite3*", - "*", - "int", - "int", - "**", - "**" - ]) - }; - capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail) { - if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_prepare_v3", f.length); - const [xSql, xSqlLen] = __flexiString(sql, Number(sqlLen)); - switch (typeof xSql) { - case "string": return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); - case typeof wasm.ptr.null: return __prepare.full(pDb, wasm.ptr.coerce(xSql), xSqlLen, prepFlags, ppStmt, pzTail); - default: return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3(). typeof=" + typeof xSql); - } - }; - capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail) { - return f.length === arguments.length ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) : __dbArgcMismatch(pDb, "sqlite3_prepare_v2", f.length); - }; - } - { - const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [ - "sqlite3_stmt*", - "int", - "string", - "int", - "*" - ]); - const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [ - "sqlite3_stmt*", - "int", - "*", - "int", - "*" - ]); - /** Documented in the capi object's initializer. */ - capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_text", f.length); - else if (wasm.isPtr(text) || null === text) return __bindText(pStmt, iCol, text, nText, xDestroy); - else if (text instanceof ArrayBuffer) text = new Uint8Array(text); - else if (Array.isArray(pMem)) text = pMem.join(""); - let p, n; - try { - if (util.isSQLableTypedArray(text)) { - p = wasm.allocFromTypedArray(text); - n = text.byteLength; - } else if ("string" === typeof text) [p, n] = wasm.allocCString(text); - else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_text()."); - return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); - } catch (e) { - wasm.dealloc(p); - return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); - } - }; - /** Documented in the capi object's initializer. */ - capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy) { - if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_blob", f.length); - else if (wasm.isPtr(pMem) || null === pMem) return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy); - else if (pMem instanceof ArrayBuffer) pMem = new Uint8Array(pMem); - else if (Array.isArray(pMem)) pMem = pMem.join(""); - let p, n; - try { - if (util.isBindableTypedArray(pMem)) { - p = wasm.allocFromTypedArray(pMem); - n = nMem >= 0 ? nMem : pMem.byteLength; - } else if ("string" === typeof pMem) [p, n] = wasm.allocCString(pMem); - else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_blob()."); - return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); - } catch (e) { - wasm.dealloc(p); - return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e); - } - }; - } - if (!capi.sqlite3_column_text) { - const argStmt = wasm.xWrap.argAdapter("sqlite3_stmt*"), argInt = wasm.xWrap.argAdapter("int"), argValue = wasm.xWrap.argAdapter("sqlite3_value*"), newStr = (cstr, n) => wasm.typedArrayToString(wasm.heap8u(), Number(cstr), Number(cstr) + n); - capi.sqlite3_column_text = function(stmt, colIndex) { - const a0 = argStmt(stmt), a1 = argInt(colIndex); - const cstr = wasm.exports.sqlite3_column_text(a0, a1); - return cstr ? newStr(cstr, wasm.exports.sqlite3_column_bytes(a0, a1)) : null; - }; - capi.sqlite3_value_text = function(val) { - const a0 = argValue(val); - const cstr = wasm.exports.sqlite3_value_text(a0); - return cstr ? newStr(cstr, wasm.exports.sqlite3_value_bytes(a0)) : null; - }; - } - /** - Wraps a small subset of the C API's sqlite3_config() options. - Unsupported options trigger the return of capi.SQLITE_NOTFOUND. - Passing fewer than 2 arguments triggers return of - capi.SQLITE_MISUSE. - */ - capi.sqlite3_config = function(op, ...args) { - if (arguments.length < 2) return capi.SQLITE_MISUSE; - switch (op) { - case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: - case capi.SQLITE_CONFIG_MEMSTATUS: - case capi.SQLITE_CONFIG_SMALL_MALLOC: - case capi.SQLITE_CONFIG_SORTERREF_SIZE: - case capi.SQLITE_CONFIG_STMTJRNL_SPILL: - case capi.SQLITE_CONFIG_URI: return wasm.exports.sqlite3__wasm_config_i(op, args[0]); - case capi.SQLITE_CONFIG_LOOKASIDE: return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]); - case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: return wasm.exports.sqlite3__wasm_config_j(op, args[0]); - case capi.SQLITE_CONFIG_GETMALLOC: - case capi.SQLITE_CONFIG_GETMUTEX: - case capi.SQLITE_CONFIG_GETPCACHE2: - case capi.SQLITE_CONFIG_GETPCACHE: - case capi.SQLITE_CONFIG_HEAP: - case capi.SQLITE_CONFIG_LOG: - case capi.SQLITE_CONFIG_MALLOC: - case capi.SQLITE_CONFIG_MMAP_SIZE: - case capi.SQLITE_CONFIG_MULTITHREAD: - case capi.SQLITE_CONFIG_MUTEX: - case capi.SQLITE_CONFIG_PAGECACHE: - case capi.SQLITE_CONFIG_PCACHE2: - case capi.SQLITE_CONFIG_PCACHE: - case capi.SQLITE_CONFIG_PCACHE_HDRSZ: - case capi.SQLITE_CONFIG_PMASZ: - case capi.SQLITE_CONFIG_SERIALIZED: - case capi.SQLITE_CONFIG_SINGLETHREAD: - case capi.SQLITE_CONFIG_SQLLOG: - case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: - default: return capi.SQLITE_NOTFOUND; - } - }; - { - const __autoExtFptr = /* @__PURE__ */ new Set(); - capi.sqlite3_auto_extension = function(fPtr) { - if (fPtr instanceof Function) fPtr = wasm.installFunction("i(ppp)", fPtr); - else if (1 !== arguments.length || !wasm.isPtr(fPtr)) return capi.SQLITE_MISUSE; - const rc = wasm.exports.sqlite3_auto_extension(fPtr); - if (fPtr !== arguments[0]) if (0 === rc) __autoExtFptr.add(fPtr); - else wasm.uninstallFunction(fPtr); - return rc; - }; - capi.sqlite3_cancel_auto_extension = function(fPtr) { - if (!fPtr || 1 !== arguments.length || !wasm.isPtr(fPtr)) return 0; - return wasm.exports.sqlite3_cancel_auto_extension(fPtr); - }; - capi.sqlite3_reset_auto_extension = function() { - wasm.exports.sqlite3_reset_auto_extension(); - for (const fp of __autoExtFptr) wasm.uninstallFunction(fp); - __autoExtFptr.clear(); - }; - } - wasm.xWrap.FuncPtrAdapter.warnOnUse = true; - const StructBinder = sqlite3.StructBinder; - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructBinder.StructType - target object. - - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error, e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc. - - As a special case, if the given function is a pointer, then - `wasm.functionEntry()` is used to validate that it is a known - function. If so, it is used as-is with no extra level of proxying - or cleanup, else an exception is thrown. It is legal to pass a - value of 0, indicating a NULL pointer, with the caveat that 0 - _is_ a legal function pointer in WASM but it will not be accepted - as such _here_. (Justification: the function at address zero must - be one which initially came from the WASM module, not a method we - want to bind to a virtual table or VFS.) - - This function returns a proxy for itself which is bound to tgt - and takes 2 args (name,func). That function returns the same - thing as this one, permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - - ACHTUNG: because we cannot generically know how to transform JS - exceptions into result codes, the installed functions do no - automatic catching of exceptions. It is critical, to avoid - undefined behavior in the C layer, that methods mapped via - this function do not throw. The exception, as it were, to that - rule is... - - If applyArgcCheck is true then each JS function (as opposed to - function pointers) gets wrapped in a proxy which asserts that it - is passed the expected number of arguments, throwing if the - argument count does not match expectations. That is only intended - for dev-time usage for sanity checking, and may leave the C - environment in an undefined state. - */ - const installMethod = function callee(tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck) { - if (!(tgt instanceof StructBinder.StructType)) toss("Usage error: target object is-not-a StructType."); - else if (!(func instanceof Function) && !wasm.isPtr(func)) toss("Usage error: expecting a Function or WASM pointer to one."); - if (1 === arguments.length) return (n, f) => callee(tgt, n, f, applyArgcCheck); - if (!callee.argcProxy) { - callee.argcProxy = function(tgt, funcName, func, sig) { - return function(...args) { - if (func.length !== arguments.length) toss("Argument mismatch for", tgt.structInfo.name + "::" + funcName + ": Native signature is:", sig); - return func.apply(this, args); - }; - }; - callee.removeFuncList = function() { - if (this.ondispose.__removeFuncList) { - this.ondispose.__removeFuncList.forEach((v, ndx) => { - if (wasm.isPtr(v)) try { - wasm.uninstallFunction(v); - } catch (e) {} - }); - delete this.ondispose.__removeFuncList; - } - }; - } - const sigN = tgt.memberSignature(name); - if (sigN.length < 2) toss("Member", name, "does not have a function pointer signature:", sigN); - const memKey = tgt.memberKey(name); - const fProxy = applyArgcCheck && !wasm.isPtr(func) ? callee.argcProxy(tgt, memKey, func, sigN) : func; - if (wasm.isPtr(fProxy)) { - if (fProxy && !wasm.functionEntry(fProxy)) toss("Pointer", fProxy, "is not a WASM function table entry."); - tgt[memKey] = fProxy; - } else { - const pFunc = wasm.installFunction(fProxy, sigN); - tgt[memKey] = pFunc; - if (!tgt.ondispose || !tgt.ondispose.__removeFuncList) { - tgt.addOnDispose("ondispose.__removeFuncList handler", callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - } - return (n, f) => callee(tgt, n, f, applyArgcCheck); - }; - installMethod.installMethodArgcCheck = false; - /** - Installs methods into the given StructBinder.StructType-type - instance. Each entry in the given methods object must map to a - known member of the given StructType, else an exception will be - triggered. See installMethod() for more details, including the - semantics of the 3rd argument. - - As an exception to the above, if any two or more methods in the - 2nd argument are the exact same function, installMethod() is - _not_ called for the 2nd and subsequent instances, and instead - those instances get assigned the same method pointer which is - created for the first instance. This optimization is primarily to - accommodate special handling of sqlite3_module::xConnect and - xCreate methods. - - On success, returns its first argument. Throws on error. - */ - const installMethods = function(structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck) { - const seen = /* @__PURE__ */ new Map(); - for (const k of Object.keys(methods)) { - const m = methods[k]; - const prior = seen.get(m); - if (prior) { - const mkey = structInstance.memberKey(k); - structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; - } else { - installMethod(structInstance, k, m, applyArgcCheck); - seen.set(m, k); - } - } - return structInstance; - }; - /** - Equivalent to calling installMethod(this,...arguments) with a - first argument of this object. If called with 1 or 2 arguments - and the first is an object, it's instead equivalent to calling - installMethods(this,...arguments). - */ - StructBinder.StructType.prototype.installMethod = function callee(name, func, applyArgcCheck = installMethod.installMethodArgcCheck) { - return arguments.length < 3 && name && "object" === typeof name ? installMethods(this, ...arguments) : installMethod(this, ...arguments); - }; - /** - Equivalent to calling installMethods() with a first argument - of this object. - */ - StructBinder.StructType.prototype.installMethods = function(methods, applyArgcCheck = installMethod.installMethodArgcCheck) { - return installMethods(this, methods, applyArgcCheck); - }; - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - const toss3 = (...args) => { - throw new sqlite3.SQLite3Error(...args); - }; - const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; - const outWrapper = function(f) { - return (...args) => f("sqlite3.oo1:", ...args); - }; - sqlite3.__isUnderTest ? outWrapper(console.debug.bind(console)) : outWrapper(sqlite3.config.debug); - sqlite3.__isUnderTest ? outWrapper(console.warn.bind(console)) : outWrapper(sqlite3.config.warn); - sqlite3.__isUnderTest ? outWrapper(console.error.bind(console)) : outWrapper(sqlite3.config.error); - /** - In order to keep clients from manipulating, perhaps - inadvertently, the underlying pointer values of DB and Stmt - instances, we'll gate access to them via the `pointer` property - accessor and store their real values in this map. Keys = DB/Stmt - objects, values = pointer values. This also unifies how those are - accessed, for potential use downstream via custom - wasm.xWrap() function signatures which know how to extract - it. - */ - const __ptrMap = /* @__PURE__ */ new WeakMap(); - /** - A Set of oo1.DB or oo1.Stmt objects which are proxies for - (sqlite3*) resp. (sqlite3_stmt*) pointers which themselves are - owned elsewhere. Objects in this Set do not own their underlying - handle and that handle must be guaranteed (by the client) to - outlive the proxy. DB.close()/Stmt.finalize() methods will remove - the object from this Set _instead_ of closing/finalizing the - pointer. These proxies are primarily intended as a way to briefly - wrap an (sqlite3[_stmt]*) object as an oo1.DB/Stmt without taking - over ownership, to take advantage of simplifies usage compared to - the C API while not imposing any change of ownership. - - See DB.wrapHandle() and Stmt.wrapHandle(). - */ - const __doesNotOwnHandle = /* @__PURE__ */ new Set(); - /** - Map of DB instances to objects, each object being a map of Stmt - wasm pointers to Stmt objects. - */ - const __stmtMap = /* @__PURE__ */ new WeakMap(); - /** If object opts has _its own_ property named p then that - property's value is returned, else dflt is returned. */ - const getOwnOption = (opts, p, dflt) => { - const d = Object.getOwnPropertyDescriptor(opts, p); - return d ? d.value : dflt; - }; - const checkSqlite3Rc = function(dbPtr, sqliteResultCode) { - if (sqliteResultCode) { - if (dbPtr instanceof DB) dbPtr = dbPtr.pointer; - toss3(sqliteResultCode, "sqlite3 result code", sqliteResultCode + ":", dbPtr ? capi.sqlite3_errmsg(dbPtr) : capi.sqlite3_errstr(sqliteResultCode)); - } - return arguments[0]; - }; - /** - sqlite3_trace_v2() callback which gets installed by the DB ctor - if its open-flags contain "t". - */ - const __dbTraceToConsole = wasm.installFunction("i(ippp)", function(t, c, p, x) { - if (capi.SQLITE_TRACE_STMT === t) console.log("SQL TRACE #" + ++this.counter, "via sqlite3@" + c + "[" + capi.sqlite3_db_filename(c, null) + "]", wasm.cstrToJs(x)); - }.bind({ counter: 0 })); - /** - A map of sqlite3_vfs pointers to SQL code or a callback function - to run when the DB constructor opens a database with the given - VFS. In the latter case, the call signature is - (theDbObject,sqlite3Namespace) and the callback is expected to - throw on error. - */ - const __vfsPostOpenCallback = Object.create(null); - /** - A proxy for DB class constructors. It must be called with the - being-construct DB object as its "this". See the DB constructor - for the argument docs. This is split into a separate function - in order to enable simple creation of special-case DB constructors, - e.g. JsStorageDb and OpfsDb. - - Expects to be passed a configuration object with the following - properties: - - - `.filename`: the db filename. It may be a special name like ":memory:" - or "". It may also be a URI-style name. - - - `.flags`: as documented in the DB constructor. - - - `.vfs`: as documented in the DB constructor. - - It also accepts those as the first 3 arguments. - - In non-default builds it may accept additional configuration - options. - */ - const dbCtorHelper = function ctor(...args) { - const opt = ctor.normalizeArgs(...args); - let pDb; - if (pDb = opt["sqlite3*"]) { - if (!opt["sqlite3*:takeOwnership"]) __doesNotOwnHandle.add(this); - this.filename = capi.sqlite3_db_filename(pDb, "main"); - } else { - let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags; - if ("string" !== typeof fn && !wasm.isPtr(fn) || "string" !== typeof flagsStr || vfsName && "string" !== typeof vfsName && !wasm.isPtr(vfsName)) { - sqlite3.config.error("Invalid DB ctor args", opt, arguments); - toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt); - } - let oflags = 0; - if (flagsStr.indexOf("c") >= 0) oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; - if (flagsStr.indexOf("w") >= 0) oflags |= capi.SQLITE_OPEN_READWRITE; - if (0 === oflags) oflags |= capi.SQLITE_OPEN_READONLY; - oflags |= capi.SQLITE_OPEN_EXRESCODE; - const stack = wasm.pstack.pointer; - try { - const pPtr = wasm.pstack.allocPtr(); - let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || wasm.ptr.null); - pDb = wasm.peekPtr(pPtr); - checkSqlite3Rc(pDb, rc); - capi.sqlite3_extended_result_codes(pDb, 1); - if (flagsStr.indexOf("t") >= 0) capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, pDb); - } catch (e) { - if (pDb) capi.sqlite3_close_v2(pDb); - throw e; - } finally { - wasm.pstack.restore(stack); - } - this.filename = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn; - } - __ptrMap.set(this, pDb); - __stmtMap.set(this, Object.create(null)); - if (!opt["sqlite3*"]) try { - const postInitSql = __vfsPostOpenCallback[capi.sqlite3_js_db_vfs(pDb) || toss3("Internal error: cannot get VFS for new db handle.")]; - if (postInitSql) - /** - Reminder: if this db is encrypted and the client did _not_ pass - in the key, any init code will fail, causing the ctor to throw. - We don't actually know whether the db is encrypted, so we cannot - sensibly apply any heuristics which skip the init code only for - encrypted databases for which no key has yet been supplied. - */ - if (postInitSql instanceof Function) postInitSql(this, sqlite3); - else checkSqlite3Rc(pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)); - } catch (e) { - this.close(); - throw e; - } - }; - /** - Sets a callback which should be called after a db is opened with - the given sqlite3_vfs pointer. The 2nd argument must be a - function, which gets called with - (theOo1DbObject,sqlite3Namespace) at the end of the DB() - constructor. The function must throw on error, in which case the - db is closed and the exception is propagated. This function is - intended only for use by DB subclasses or sqlite3_vfs - implementations. - - Prior to 2024-07-22, it was legal to pass SQL code as the second - argument, but that can interfere with a client's ability to run - pragmas which must be run before anything else, namely (pragma - locking_mode=exclusive) for use with WAL mode. That capability - had only ever been used as an internal detail of the two OPFS - VFSes, and they no longer use it that way. - */ - dbCtorHelper.setVfsPostOpenCallback = function(pVfs, callback) { - if (!(callback instanceof Function)) toss3("dbCtorHelper.setVfsPostOpenCallback() should not be used with a non-function argument.", arguments); - __vfsPostOpenCallback[pVfs] = callback; - }; - /** - A helper for DB constructors. It accepts either a single - config-style object or up to 3 arguments (filename, dbOpenFlags, - dbVfsName). It returns a new object containing: - - { filename: ..., flags: ..., vfs: ... } - - If passed an object, any additional properties it has are copied - as-is into the new object. - */ - dbCtorHelper.normalizeArgs = function(filename = ":memory:", flags = "c", vfs = null) { - const arg = {}; - if (1 === arguments.length && arguments[0] && "object" === typeof arguments[0]) { - Object.assign(arg, arguments[0]); - if (void 0 === arg.flags) arg.flags = "c"; - if (void 0 === arg.vfs) arg.vfs = null; - if (void 0 === arg.filename) arg.filename = ":memory:"; - } else { - arg.filename = filename; - arg.flags = flags; - arg.vfs = vfs; - } - return arg; - }; - /** - The DB class provides a high-level OO wrapper around an sqlite3 - db handle. - - The given db filename must be resolvable using whatever - filesystem layer (virtual or otherwise) is set up for the default - sqlite3 VFS or a VFS which can resolve it must be specified. - - The special sqlite3 db names ":memory:" and "" (temporary db) - have their normal special meanings here and need not resolve to - real filenames, but "" uses an on-storage temporary database and - requires that the VFS support that. - - The second argument specifies the open/create mode for the - database. It must be string containing a sequence of letters (in - any order, but case sensitive) specifying the mode: - - - "c": create if it does not exist, else fail if it does not - exist. Implies the "w" flag. - - - "w": write. Implies "r": a db cannot be write-only. - - - "r": read-only if neither "w" nor "c" are provided, else it - is ignored. - - - "t": enable tracing of SQL executed on this database handle, - sending it to `console.log()`. To disable it later, call - `sqlite3.capi.sqlite3_trace_v2(thisDb.pointer, 0, 0, 0)`. - - If "w" is not provided, the db is implicitly read-only, noting - that "rc" is meaningless - - Any other letters are currently ignored. The default is - "c". These modes are ignored for the special ":memory:" and "" - names and _may_ be ignored altogether for certain VFSes. - - The final argument is analogous to the final argument of - sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value, - or none at all, to use the default. If passed a value, it must - be the string name of a VFS. - - The constructor optionally (and preferably) takes its arguments - in the form of a single configuration object with the following - properties: - - - `filename`: database file name - - `flags`: open-mode flags - - `vfs`: the VFS fname - - - The `filename` and `vfs` arguments may be either JS strings or - C-strings allocated via WASM. `flags` is required to be a JS - string (because it's specific to this API, which is specific - to JS). - - For purposes of passing a DB instance to C-style sqlite3 - functions, the DB object's read-only `pointer` property holds its - `sqlite3*` pointer value. That property can also be used to check - whether this DB instance is still open: it will evaluate to - `undefined` after the DB object's close() method is called. - - In the main window thread, the filenames `":localStorage:"` and - `":sessionStorage:"` are special: they cause the db to use either - localStorage or sessionStorage for storing the database using - the kvvfs. If one of these names are used, they trump - any vfs name set in the arguments. - */ - const DB = function(...args) { - dbCtorHelper.apply(this, args); - }; - DB.dbCtorHelper = dbCtorHelper; - /** - Internal-use enum for mapping JS types to DB-bindable types. - These do not (and need not) line up with the SQLITE_type - values. All values in this enum must be truthy and (mostly) - distinct but they need not be numbers. - */ - const BindTypes = { - null: 1, - number: 2, - string: 3, - boolean: 4, - blob: 5 - }; - if (wasm.bigIntEnabled) BindTypes.bigint = BindTypes.number; - /** - This class wraps sqlite3_stmt. Calling this constructor - directly will trigger an exception. Use DB.prepare() to create - new instances. - - For purposes of passing a Stmt instance to C-style sqlite3 - functions, its read-only `pointer` property holds its `sqlite3_stmt*` - pointer value. - - Other non-function properties include: - - - `db`: the DB object which created the statement. - - - `columnCount`: the number of result columns in the query, or 0 - for queries which cannot return results. This property is a - read-only proxy for sqlite3_column_count() and its use in loops - should be avoided because of the call overhead associated with - that. The `columnCount` is not cached when the Stmt is created - because a schema change made between this statement's preparation - and when it is stepped may invalidate it. - - - `parameterCount`: the number of bindable parameters in the - query. Like `columnCount`, this property is ready-only and is a - proxy for a C API call. - - As a general rule, most methods of this class will throw if - called on an instance which has been finalized. For brevity's - sake, the method docs do not all repeat this warning. - */ - const Stmt = function() { - if (BindTypes !== arguments[2]) toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare()."); - this.db = arguments[0]; - __ptrMap.set(this, arguments[1]); - if (arguments.length > 3 && !arguments[3]) __doesNotOwnHandle.add(this); - }; - /** Throws if the given DB has been closed, else it is returned. */ - const affirmDbOpen = function(db) { - if (!db.pointer) toss3("DB has been closed."); - return db; - }; - /** Throws if ndx is not an integer or if it is out of range - for stmt.columnCount, else returns stmt. - - Reminder: this will also fail after the statement is finalized - but the resulting error will be about an out-of-bounds column - index rather than a statement-is-finalized error. - */ - const affirmColIndex = function(stmt, ndx) { - if (ndx !== (ndx | 0) || ndx < 0 || ndx >= stmt.columnCount) toss3("Column index", ndx, "is out of range."); - return stmt; - }; - /** - Expects to be passed the `arguments` object from DB.exec(). Does - the argument processing/validation, throws on error, and returns - a new object on success: - - { sql: the SQL, opt: optionsObj, cbArg: function} - - The opt object is a normalized copy of any passed to this - function. The sql will be converted to a string if it is provided - in one of the supported non-string formats. - - cbArg is only set if the opt.callback or opt.resultRows are set, - in which case it's a function which expects to be passed the - current Stmt and returns the callback argument of the type - indicated by the input arguments. - */ - const parseExecArgs = function(db, args) { - const out = Object.create(null); - out.opt = Object.create(null); - switch (args.length) { - case 1: - if ("string" === typeof args[0] || util.isSQLableTypedArray(args[0])) out.sql = args[0]; - else if (Array.isArray(args[0])) out.sql = args[0]; - else if (args[0] && "object" === typeof args[0]) { - out.opt = args[0]; - out.sql = out.opt.sql; - } - break; - case 2: - out.sql = args[0]; - out.opt = args[1]; - break; - default: toss3("Invalid argument count for exec()."); - } - out.sql = util.flexibleString(out.sql); - if ("string" !== typeof out.sql) toss3("Missing SQL argument or unsupported SQL value type."); - const opt = out.opt; - switch (opt.returnValue) { - case "resultRows": - if (!opt.resultRows) opt.resultRows = []; - out.returnVal = () => opt.resultRows; - break; - case "saveSql": - if (!opt.saveSql) opt.saveSql = []; - out.returnVal = () => opt.saveSql; - break; - case void 0: - case "this": - out.returnVal = () => db; - break; - default: toss3("Invalid returnValue value:", opt.returnValue); - } - if (!opt.callback && !opt.returnValue && void 0 !== opt.rowMode) { - if (!opt.resultRows) opt.resultRows = []; - out.returnVal = () => opt.resultRows; - } - if (opt.callback || opt.resultRows) switch (void 0 === opt.rowMode ? "array" : opt.rowMode) { - case "object": - out.cbArg = (stmt, cache) => { - if (!cache.columnNames) cache.columnNames = stmt.getColumnNames([]); - const row = stmt.get([]); - const rv = Object.create(null); - for (const i in cache.columnNames) rv[cache.columnNames[i]] = row[i]; - return rv; - }; - break; - case "array": - out.cbArg = (stmt) => stmt.get([]); - break; - case "stmt": - if (Array.isArray(opt.resultRows)) toss3("exec(): invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", "a result column number, or column name reference."); - out.cbArg = (stmt) => stmt; - break; - default: - if (util.isInt32(opt.rowMode)) { - out.cbArg = (stmt) => stmt.get(opt.rowMode); - break; - } else if ("string" === typeof opt.rowMode && opt.rowMode.length > 1 && "$" === opt.rowMode[0]) { - const $colName = opt.rowMode.substr(1); - out.cbArg = (stmt) => { - const rc = stmt.get(Object.create(null))[$colName]; - return void 0 === rc ? toss3(capi.SQLITE_NOTFOUND, "exec(): unknown result column:", $colName) : rc; - }; - break; - } - toss3("Invalid rowMode:", opt.rowMode); - } - return out; - }; - /** - Internal impl of the DB.selectValue(), selectArray(), and - selectObject() methods. - */ - const __selectFirstRow = (db, sql, bind, ...getArgs) => { - const stmt = db.prepare(sql); - try { - const rc = stmt.bind(bind).step() ? stmt.get(...getArgs) : void 0; - stmt.reset(); - return rc; - } finally { - stmt.finalize(); - } - }; - /** - Internal impl of the DB.selectArrays() and selectObjects() - methods. - */ - const __selectAll = (db, sql, bind, rowMode) => db.exec({ - sql, - bind, - rowMode, - returnValue: "resultRows" - }); - /** - Expects to be given a DB instance or an `sqlite3*` pointer (may - be null) and an sqlite3 API result code. If the result code is - not falsy, this function throws an SQLite3Error with an error - message from sqlite3_errmsg(), using db (or, if db is-a DB, - db.pointer) as the db handle, or sqlite3_errstr() if db is - falsy. Note that if it's passed a non-error code like SQLITE_ROW - or SQLITE_DONE, it will still throw but the error string might be - "Not an error." The various non-0 non-error codes need to be - checked for in client code where they are expected. - - The thrown exception's `resultCode` property will be the value of - the second argument to this function. - - If it does not throw, it returns its first argument. - */ - DB.checkRc = (db, resultCode) => checkSqlite3Rc(db, resultCode); - DB.prototype = { - isOpen: function() { - return !!this.pointer; - }, - affirmOpen: function() { - return affirmDbOpen(this); - }, - close: function() { - const pDb = this.pointer; - if (pDb) { - if (this.onclose && this.onclose.before instanceof Function) try { - this.onclose.before(this); - } catch (e) {} - Object.keys(__stmtMap.get(this)).forEach((k, s) => { - if (s && s.pointer) try { - s.finalize(); - } catch (e) {} - }); - __ptrMap.delete(this); - __stmtMap.delete(this); - if (!__doesNotOwnHandle.delete(this)) capi.sqlite3_close_v2(pDb); - if (this.onclose && this.onclose.after instanceof Function) try { - this.onclose.after(this); - } catch (e) {} - delete this.filename; - } - }, - changes: function(total = false, sixtyFour = false) { - const p = affirmDbOpen(this).pointer; - if (total) return sixtyFour ? capi.sqlite3_total_changes64(p) : capi.sqlite3_total_changes(p); - else return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p); - }, - dbFilename: function(dbName = "main") { - return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); - }, - dbName: function(dbNumber = 0) { - return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); - }, - dbVfsName: function(dbName = 0) { - let rc; - const pVfs = capi.sqlite3_js_db_vfs(affirmDbOpen(this).pointer, dbName); - if (pVfs) { - const v = new capi.sqlite3_vfs(pVfs); - try { - rc = wasm.cstrToJs(v.$zName); - } finally { - v.dispose(); - } - } - return rc; - }, - prepare: function(sql) { - affirmDbOpen(this); - const stack = wasm.pstack.pointer; - let ppStmt, pStmt; - try { - ppStmt = wasm.pstack.alloc(8); - DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); - pStmt = wasm.peekPtr(ppStmt); - } finally { - wasm.pstack.restore(stack); - } - if (!pStmt) toss3("Cannot prepare empty SQL."); - const stmt = new Stmt(this, pStmt, BindTypes); - __stmtMap.get(this)[pStmt] = stmt; - return stmt; - }, - exec: function() { - affirmDbOpen(this); - const arg = parseExecArgs(this, arguments); - if (!arg.sql) return toss3("exec() requires an SQL string."); - const opt = arg.opt; - const callback = opt.callback; - const resultRows = Array.isArray(opt.resultRows) ? opt.resultRows : void 0; - let stmt; - let bind = opt.bind; - let evalFirstResult = !!(arg.cbArg || opt.columnNames || resultRows); - const stack = wasm.scopedAllocPush(); - const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : void 0; - try { - const isTA = util.isSQLableTypedArray(arg.sql); - let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); - const ppStmt = wasm.scopedAlloc(2 * wasm.ptr.size + (sqlByteLen + 1)); - const pzTail = wasm.ptr.add(ppStmt, wasm.ptr.size); - let pSql = wasm.ptr.add(pzTail, wasm.ptr.size); - const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen); - if (isTA) wasm.heap8().set(arg.sql, pSql); - else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); - wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0); - while (pSql && wasm.peek8(pSql)) { - wasm.pokePtr([ppStmt, pzTail], 0); - DB.checkRc(this, capi.sqlite3_prepare_v3(this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail)); - const pStmt = wasm.peekPtr(ppStmt); - pSql = wasm.peekPtr(pzTail); - sqlByteLen = Number(wasm.ptr.add(pSqlEnd, -pSql)); - if (!pStmt) continue; - if (saveSql) saveSql.push(capi.sqlite3_sql(pStmt).trim()); - stmt = new Stmt(this, pStmt, BindTypes); - if (bind && stmt.parameterCount) { - stmt.bind(bind); - bind = null; - } - if (evalFirstResult && stmt.columnCount) { - let gotColNames = Array.isArray(opt.columnNames) ? 0 : 1; - evalFirstResult = false; - if (arg.cbArg || resultRows) { - const cbArgCache = Object.create(null); - for (; stmt.step(); __execLock.delete(stmt)) { - if (0 === gotColNames++) stmt.getColumnNames(cbArgCache.columnNames = opt.columnNames || []); - __execLock.add(stmt); - const row = arg.cbArg(stmt, cbArgCache); - if (resultRows) resultRows.push(row); - if (callback && false === callback.call(opt, row, stmt)) break; - } - __execLock.delete(stmt); - } - if (0 === gotColNames) stmt.getColumnNames(opt.columnNames); - } else stmt.step(); - stmt.reset().finalize(); - stmt = null; - } - } finally { - if (stmt) { - __execLock.delete(stmt); - stmt.finalize(); - } - wasm.scopedAllocPop(stack); - } - return arg.returnVal(); - }, - createFunction: function f(name, xFunc, opt) { - const isFunc = (f) => f instanceof Function; - switch (arguments.length) { - case 1: - opt = name; - name = opt.name; - xFunc = opt.xFunc || 0; - break; - case 2: - if (!isFunc(xFunc)) { - opt = xFunc; - xFunc = opt.xFunc || 0; - } - break; - case 3: break; - default: break; - } - if (!opt) opt = {}; - if ("string" !== typeof name) toss3("Invalid arguments: missing function name."); - let xStep = opt.xStep || 0; - let xFinal = opt.xFinal || 0; - const xValue = opt.xValue || 0; - const xInverse = opt.xInverse || 0; - let isWindow = void 0; - if (isFunc(xFunc)) { - isWindow = false; - if (isFunc(xStep) || isFunc(xFinal)) toss3("Ambiguous arguments: scalar or aggregate?"); - xStep = xFinal = null; - } else if (isFunc(xStep)) { - if (!isFunc(xFinal)) toss3("Missing xFinal() callback for aggregate or window UDF."); - xFunc = null; - } else if (isFunc(xFinal)) toss3("Missing xStep() callback for aggregate or window UDF."); - else toss3("Missing function-type properties."); - if (false === isWindow) { - if (isFunc(xValue) || isFunc(xInverse)) toss3("xValue and xInverse are not permitted for non-window UDFs."); - } else if (isFunc(xValue)) { - if (!isFunc(xInverse)) toss3("xInverse must be provided if xValue is."); - isWindow = true; - } else if (isFunc(xInverse)) toss3("xValue must be provided if xInverse is."); - const pApp = opt.pApp; - if (void 0 !== pApp && null !== pApp && !wasm.isPtr(pApp)) toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); - const xDestroy = opt.xDestroy || 0; - if (xDestroy && !isFunc(xDestroy)) toss3("xDestroy property must be a function."); - let fFlags = 0; - if (getOwnOption(opt, "deterministic")) fFlags |= capi.SQLITE_DETERMINISTIC; - if (getOwnOption(opt, "directOnly")) fFlags |= capi.SQLITE_DIRECTONLY; - if (getOwnOption(opt, "innocuous")) fFlags |= capi.SQLITE_INNOCUOUS; - name = name.toLowerCase(); - const xArity = xFunc || xStep; - const arity = getOwnOption(opt, "arity"); - const arityArg = "number" === typeof arity ? arity : xArity.length ? xArity.length - 1 : 0; - let rc; - if (isWindow) rc = capi.sqlite3_create_window_function(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xStep, xFinal, xValue, xInverse, xDestroy); - else rc = capi.sqlite3_create_function_v2(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xFunc, xStep, xFinal, xDestroy); - DB.checkRc(this, rc); - return this; - }, - selectValue: function(sql, bind, asType) { - return __selectFirstRow(this, sql, bind, 0, asType); - }, - selectValues: function(sql, bind, asType) { - const stmt = this.prepare(sql), rc = []; - try { - stmt.bind(bind); - while (stmt.step()) rc.push(stmt.get(0, asType)); - stmt.reset(); - } finally { - stmt.finalize(); - } - return rc; - }, - selectArray: function(sql, bind) { - return __selectFirstRow(this, sql, bind, []); - }, - selectObject: function(sql, bind) { - return __selectFirstRow(this, sql, bind, {}); - }, - selectArrays: function(sql, bind) { - return __selectAll(this, sql, bind, "array"); - }, - selectObjects: function(sql, bind) { - return __selectAll(this, sql, bind, "object"); - }, - openStatementCount: function() { - return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; - }, - transaction: function(callback) { - let opener = "BEGIN"; - if (arguments.length > 1) { - if (/[^a-zA-Z]/.test(arguments[0])) toss3(capi.SQLITE_MISUSE, "Invalid argument for BEGIN qualifier."); - opener += " " + arguments[0]; - callback = arguments[1]; - } - affirmDbOpen(this).exec(opener); - try { - const rc = callback(this); - this.exec("COMMIT"); - return rc; - } catch (e) { - this.exec("ROLLBACK"); - throw e; - } - }, - savepoint: function(callback) { - affirmDbOpen(this).exec("SAVEPOINT oo1"); - try { - const rc = callback(this); - this.exec("RELEASE oo1"); - return rc; - } catch (e) { - this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); - throw e; - } - }, - checkRc: function(resultCode) { - return checkSqlite3Rc(this, resultCode); - } - }; - /** - Returns a new oo1.DB instance which wraps the given (sqlite3*) - WASM pointer, optionally with or without taking over ownership of - that pointer. - - The first argument must be either a non-NULL (sqlite3*) WASM - pointer. - - The second argument, defaulting to false, specifies ownership of - the first argument. If it is truthy, the returned object will - pass that pointer to sqlite3_close() when its close() method is - called, otherwise it will not. - - Throws if pDb is not a non-0 WASM pointer. - - The caller MUST GUARANTEE that the passed-in handle will outlive - the returned object, i.e. that it will not be closed. If it is closed, - this object will hold a stale pointer and results are undefined. - - Aside from its lifetime, the proxy is to be treated as any other - DB instance, including the requirement of calling close() on - it. close() will free up internal resources owned by the proxy - and disassociate the proxy from that handle but will not - actually close the proxied db handle unless this function is - passed a thruthy second argument. - - To stress: - - - DO NOT call sqlite3_close() (or similar) on the being-proxied - pointer while a proxy is active. - - - ALWAYS eventually call close() on the returned object. If the - proxy does not own the underlying handle then its MUST be - closed BEFORE the being-proxied handle is closed. - - Design notes: - - - wrapHandle() "could" accept a DB object instance as its first - argument and proxy thatDb.pointer but there is currently no use - case where doing so would be useful, so it does not allow - that. That restriction may be lifted in a future version. - */ - DB.wrapHandle = function(pDb, takeOwnership = false) { - if (!pDb || !wasm.isPtr(pDb)) throw new sqlite3.SQLite3Error(capi.SQLITE_MISUSE, "Argument must be a WASM sqlite3 pointer"); - return new DB({ - "sqlite3*": pDb, - "sqlite3*:takeOwnership": !!takeOwnership - }); - }; - /** Throws if the given Stmt has been finalized, else stmt is - returned. */ - const affirmStmtOpen = function(stmt) { - if (!stmt.pointer) toss3("Stmt has been closed."); - return stmt; - }; - /** Returns an opaque truthy value from the BindTypes - enum if v's type is a valid bindable type, else - returns a falsy value. As a special case, a value of - undefined is treated as a bind type of null. */ - const isSupportedBindType = function(v) { - let t = BindTypes[null === v || void 0 === v ? "null" : typeof v]; - switch (t) { - case BindTypes.boolean: - case BindTypes.null: - case BindTypes.number: - case BindTypes.string: return t; - case BindTypes.bigint: return wasm.bigIntEnabled ? t : void 0; - default: return util.isBindableTypedArray(v) ? BindTypes.blob : void 0; - } - }; - /** - If isSupportedBindType(v) returns a truthy value, this - function returns that value, else it throws. - */ - const affirmSupportedBindType = function(v) { - return isSupportedBindType(v) || toss3("Unsupported bind() argument type:", typeof v); - }; - /** - If key is a number and within range of stmt's bound parameter - count, key is returned. - - If key is not a number then it must be a JS string (not a WASM - string) and it is checked against named parameters. If a match is - found, its index is returned. - - Else it throws. - */ - const affirmParamIndex = function(stmt, key) { - const n = "number" === typeof key ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); - if (0 === n || !util.isInt32(n)) toss3("Invalid bind() parameter name: " + key); - else if (n < 1 || n > stmt.parameterCount) toss3("Bind index", key, "is out of range."); - return n; - }; - /** - Each Stmt object which is "locked" by DB.exec() gets an entry - here to note that "lock". - - The reason this is in place is because exec({callback:...})'s - callback gets access to the Stmt objects created internally by - exec() but it must not use certain Stmt APIs. - */ - const __execLock = /* @__PURE__ */ new Set(); - /** - This is a Stmt.get() counterpart of __execLock. Each time - Stmt.step() returns true, the statement is added to this set, - indicating that Stmt.get() is legal. Stmt APIs which invalidate - that status remove the Stmt object from this set, which will - cause Stmt.get() to throw with a descriptive error message - instead of a more generic "API misuse" if we were to allow that - call to reach the C API. - */ - const __stmtMayGet = /* @__PURE__ */ new Set(); - /** - Stmt APIs which are prohibited on locked objects must call - affirmNotLockedByExec() before doing any work. - - If __execLock.has(stmt) is truthy, this throws an exception - complaining that the 2nd argument (an operation name, - e.g. "bind()") is not legal while the statement is "locked". - Locking happens before an exec()-like callback is passed a - statement, to ensure that the callback does not mutate or - finalize the statement. If it does not throw, it returns stmt. - */ - const affirmNotLockedByExec = function(stmt, currentOpName) { - if (__execLock.has(stmt)) toss3("Operation is illegal when statement is locked:", currentOpName); - return stmt; - }; - /** - Binds a single bound parameter value on the given stmt at the - given index (numeric or named) using the given bindType (see - the BindTypes enum) and value. Throws on error. Returns stmt on - success. - */ - const bindOne = function f(stmt, ndx, bindType, val) { - affirmNotLockedByExec(affirmStmtOpen(stmt), "bind()"); - if (!f._) { - f._tooBigInt = (v) => toss3("BigInt value is too big to store without precision loss:", v); - f._ = { string: function(stmt, ndx, val, asBlob) { - const [pStr, n] = wasm.allocCString(val, true); - return (asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text)(stmt.pointer, ndx, pStr, n, capi.SQLITE_WASM_DEALLOC); - } }; - } - affirmSupportedBindType(val); - ndx = affirmParamIndex(stmt, ndx); - let rc = 0; - switch (null === val || void 0 === val ? BindTypes.null : bindType) { - case BindTypes.null: - rc = capi.sqlite3_bind_null(stmt.pointer, ndx); - break; - case BindTypes.string: - rc = f._.string(stmt, ndx, val, false); - break; - case BindTypes.number: { - let m; - if (util.isInt32(val)) m = capi.sqlite3_bind_int; - else if ("bigint" === typeof val) if (!util.bigIntFits64(val)) f._tooBigInt(val); - else if (wasm.bigIntEnabled) m = capi.sqlite3_bind_int64; - else if (util.bigIntFitsDouble(val)) { - val = Number(val); - m = capi.sqlite3_bind_double; - } else f._tooBigInt(val); - else { - val = Number(val); - if (wasm.bigIntEnabled && Number.isInteger(val)) m = capi.sqlite3_bind_int64; - else m = capi.sqlite3_bind_double; - } - rc = m(stmt.pointer, ndx, val); - break; - } - case BindTypes.boolean: - rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0); - break; - case BindTypes.blob: { - if ("string" === typeof val) { - rc = f._.string(stmt, ndx, val, true); - break; - } else if (val instanceof ArrayBuffer) val = new Uint8Array(val); - else if (!util.isBindableTypedArray(val)) toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, Int8Array, or ArrayBuffer."); - const pBlob = wasm.alloc(val.byteLength || 1); - wasm.heap8().set(val.byteLength ? val : [0], Number(pBlob)); - rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC); - break; - } - default: - sqlite3.config.warn("Unsupported bind() argument type:", val); - toss3("Unsupported bind() argument type: " + typeof val); - } - if (rc) DB.checkRc(stmt.db.pointer, rc); - return stmt; - }; - Stmt.prototype = { - finalize: function() { - const ptr = this.pointer; - if (ptr) { - affirmNotLockedByExec(this, "finalize()"); - const rc = __doesNotOwnHandle.delete(this) ? 0 : capi.sqlite3_finalize(ptr); - delete __stmtMap.get(this.db)[ptr]; - __ptrMap.delete(this); - __execLock.delete(this); - __stmtMayGet.delete(this); - delete this.parameterCount; - delete this.db; - return rc; - } - }, - clearBindings: function() { - affirmNotLockedByExec(affirmStmtOpen(this), "clearBindings()"); - capi.sqlite3_clear_bindings(this.pointer); - __stmtMayGet.delete(this); - return this; - }, - reset: function(alsoClearBinds) { - affirmNotLockedByExec(this, "reset()"); - if (alsoClearBinds) this.clearBindings(); - const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer); - __stmtMayGet.delete(this); - checkSqlite3Rc(this.db, rc); - return this; - }, - bind: function() { - affirmStmtOpen(this); - let ndx, arg; - switch (arguments.length) { - case 1: - ndx = 1; - arg = arguments[0]; - break; - case 2: - ndx = arguments[0]; - arg = arguments[1]; - break; - default: toss3("Invalid bind() arguments."); - } - if (void 0 === arg) return this; - else if (!this.parameterCount) toss3("This statement has no bindable parameters."); - __stmtMayGet.delete(this); - if (null === arg) return bindOne(this, ndx, BindTypes.null, arg); - else if (Array.isArray(arg)) { - if (1 !== arguments.length) toss3("When binding an array, an index argument is not permitted."); - arg.forEach((v, i) => bindOne(this, i + 1, affirmSupportedBindType(v), v)); - return this; - } else if (arg instanceof ArrayBuffer) arg = new Uint8Array(arg); - if ("object" === typeof arg && !util.isBindableTypedArray(arg)) { - if (1 !== arguments.length) toss3("When binding an object, an index argument is not permitted."); - Object.keys(arg).forEach((k) => bindOne(this, k, affirmSupportedBindType(arg[k]), arg[k])); - return this; - } else return bindOne(this, ndx, affirmSupportedBindType(arg), arg); - toss3("Should not reach this point."); - }, - bindAsBlob: function(ndx, arg) { - affirmStmtOpen(this); - if (1 === arguments.length) { - arg = ndx; - ndx = 1; - } - const t = affirmSupportedBindType(arg); - if (BindTypes.string !== t && BindTypes.blob !== t && BindTypes.null !== t) toss3("Invalid value type for bindAsBlob()"); - return bindOne(this, ndx, BindTypes.blob, arg); - }, - step: function() { - affirmNotLockedByExec(this, "step()"); - const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); - switch (rc) { - case capi.SQLITE_DONE: - __stmtMayGet.delete(this); - return false; - case capi.SQLITE_ROW: - __stmtMayGet.add(this); - return true; - default: - __stmtMayGet.delete(this); - sqlite3.config.warn("sqlite3_step() rc=", rc, capi.sqlite3_js_rc_str(rc), "SQL =", capi.sqlite3_sql(this.pointer)); - DB.checkRc(this.db.pointer, rc); - } - }, - stepReset: function() { - this.step(); - return this.reset(); - }, - stepFinalize: function() { - try { - const rc = this.step(); - this.reset(); - return rc; - } finally { - try { - this.finalize(); - } catch (e) {} - } - }, - get: function(ndx, asType) { - if (!__stmtMayGet.has(affirmStmtOpen(this))) toss3("Stmt.step() has not (recently) returned true."); - if (Array.isArray(ndx)) { - let i = 0; - const n = this.columnCount; - while (i < n) ndx[i] = this.get(i++); - return ndx; - } else if (ndx && "object" === typeof ndx) { - let i = 0; - const n = this.columnCount; - while (i < n) ndx[capi.sqlite3_column_name(this.pointer, i)] = this.get(i++); - return ndx; - } - affirmColIndex(this, ndx); - switch (void 0 === asType ? capi.sqlite3_column_type(this.pointer, ndx) : asType) { - case capi.SQLITE_NULL: return null; - case capi.SQLITE_INTEGER: if (wasm.bigIntEnabled) { - const rc = capi.sqlite3_column_int64(this.pointer, ndx); - if (rc >= Number.MIN_SAFE_INTEGER && rc <= Number.MAX_SAFE_INTEGER) return Number(rc).valueOf(); - return rc; - } else { - const rc = capi.sqlite3_column_double(this.pointer, ndx); - if (rc > Number.MAX_SAFE_INTEGER || rc < Number.MIN_SAFE_INTEGER) toss3("Integer is out of range for JS integer range: " + rc); - return util.isInt32(rc) ? rc | 0 : rc; - } - case capi.SQLITE_FLOAT: return capi.sqlite3_column_double(this.pointer, ndx); - case capi.SQLITE_TEXT: return capi.sqlite3_column_text(this.pointer, ndx); - case capi.SQLITE_BLOB: { - const n = capi.sqlite3_column_bytes(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx), rc = new Uint8Array(n); - if (n) { - rc.set(wasm.heap8u().slice(Number(ptr), Number(ptr) + n), 0); - if (this.db._blobXfer instanceof Array) this.db._blobXfer.push(rc.buffer); - } - return rc; - } - default: toss3("Don't know how to translate", "type of result column #" + ndx + "."); - } - toss3("Not reached."); - }, - getInt: function(ndx) { - return this.get(ndx, capi.SQLITE_INTEGER); - }, - getFloat: function(ndx) { - return this.get(ndx, capi.SQLITE_FLOAT); - }, - getString: function(ndx) { - return this.get(ndx, capi.SQLITE_TEXT); - }, - getBlob: function(ndx) { - return this.get(ndx, capi.SQLITE_BLOB); - }, - getJSON: function(ndx) { - const s = this.get(ndx, capi.SQLITE_STRING); - return null === s ? s : JSON.parse(s); - }, - getColumnName: function(ndx) { - return capi.sqlite3_column_name(affirmColIndex(affirmStmtOpen(this), ndx).pointer, ndx); - }, - getColumnNames: function(tgt = []) { - affirmColIndex(affirmStmtOpen(this), 0); - const n = this.columnCount; - for (let i = 0; i < n; ++i) tgt.push(capi.sqlite3_column_name(this.pointer, i)); - return tgt; - }, - getParamIndex: function(name) { - return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_index(this.pointer, name) : void 0; - }, - getParamName: function(ndx) { - return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_name(this.pointer, ndx) : void 0; - }, - isBusy: function() { - return 0 !== capi.sqlite3_stmt_busy(affirmStmtOpen(this)); - }, - isReadOnly: function() { - return 0 !== capi.sqlite3_stmt_readonly(affirmStmtOpen(this)); - } - }; - { - const prop = { - enumerable: true, - get: function() { - return __ptrMap.get(this); - }, - set: () => toss3("The pointer property is read-only.") - }; - Object.defineProperty(Stmt.prototype, "pointer", prop); - Object.defineProperty(DB.prototype, "pointer", prop); - } - /** - Stmt.columnCount is an interceptor for sqlite3_column_count(). - - This requires an unfortunate performance hit compared to caching - columnCount when the Stmt is created/prepared (as was done in - SQLite <=3.42.0), but is necessary in order to handle certain - corner cases, as described in - https://sqlite.org/forum/forumpost/7774b773937cbe0a. - */ - Object.defineProperty(Stmt.prototype, "columnCount", { - enumerable: false, - get: function() { - return capi.sqlite3_column_count(this.pointer); - }, - set: () => toss3("The columnCount property is read-only.") - }); - Object.defineProperty(Stmt.prototype, "parameterCount", { - enumerable: false, - get: function() { - return capi.sqlite3_bind_parameter_count(this.pointer); - }, - set: () => toss3("The parameterCount property is read-only.") - }); - /** - The Stmt counterpart of oo1.DB.wrapHandle(), this creates a Stmt - instance which wraps a WASM (sqlite3_stmt*) in the oo1 API, - optionally with or without taking over ownership of that pointer. - - The first argument must be an oo1.DB instance[^1]. - - The second argument must be a valid WASM (sqlite3_stmt*), as - produced by sqlite3_prepare_v2() and sqlite3_prepare_v3(). - - The third argument, defaulting to false, specifies whether the - returned Stmt object takes over ownership of the underlying - (sqlite3_stmt*). If true, the returned object's finalize() method - will finalize that handle, else it will not. If it is false, - ownership of pStmt is unchanged and pStmt MUST outlive the - returned object or results are undefined. - - This function throws if the arguments are invalid. On success it - returns a new Stmt object which wraps the given statement - pointer. - - Like all Stmt objects, the finalize() method must eventually be - called on the returned object to free up internal resources, - regardless of whether this function's third argument is true or - not. - - [^1]: The first argument cannot be a (sqlite3*) because the - resulting Stmt object requires a parent DB object. It is not yet - determined whether it would be of general benefit to refactor the - DB/Stmt pair internals to communicate in terms of the underlying - (sqlite3*) rather than a DB object. If so, we could laxen the - first argument's requirement and allow an (sqlite3*). Because - DB.wrapHandle() enables multiple DB objects to proxy the same - (sqlite3*), we cannot unambiguously translate the first arugment - from (sqlite3*) to DB instances for us with this function's first - argument. - */ - Stmt.wrapHandle = function(oo1db, pStmt, takeOwnership = false) { - if (!(oo1db instanceof DB) || !oo1db.pointer) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "First argument must be an opened sqlite3.oo1.DB instance"); - if (!pStmt || !wasm.isPtr(pStmt)) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "Second argument must be a WASM sqlite3_stmt pointer"); - return new Stmt(oo1db, pStmt, BindTypes, !!takeOwnership); - }; - /** The OO API's public namespace. */ - sqlite3.oo1 = { - DB, - Stmt - }; - }); - /** - 2022-07-22 - - The author disclaims copyright to this source code. In place of a - legal notice, here is a blessing: - - * May you do good and not evil. - * May you find forgiveness for yourself and forgive others. - * May you share freely, never taking more than you give. - - *********************************************************************** - - This file implements the initializer for SQLite's "Worker API #1", a - very basic DB access API intended to be scripted from a main window - thread via Worker-style messages. Because of limitations in that - type of communication, this API is minimalistic and only capable of - serving relatively basic DB requests (e.g. it cannot process nested - query loops concurrently). - - This file requires that the core C-style sqlite3 API and OO API #1 - have been loaded. - */ - /** - sqlite3.initWorker1API() implements a Worker-based wrapper around - SQLite3 OO API #1, colloquially known as "Worker API #1". - - In order to permit this API to be loaded in worker threads without - automatically registering onmessage handlers, initializing the - worker API requires calling initWorker1API(). If this function is - called from a non-worker thread then it throws an exception. It - must only be called once per Worker. - - When initialized, it installs message listeners to receive Worker - messages and then it posts a message in the form: - - ``` - {type:'sqlite3-api', result:'worker1-ready'} - ``` - - to let the client know that it has been initialized. Clients may - optionally depend on this function not returning until - initialization is complete, as the initialization is synchronous. - In some contexts, however, listening for the above message is - a better fit. - - Note that the worker-based interface can be slightly quirky because - of its async nature. In particular, any number of messages may be posted - to the worker before it starts handling any of them. If, e.g., an - "open" operation fails, any subsequent messages will fail. The - Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`) - is more comfortable to use in that regard. - - The documentation for the input and output worker messages for - this API follows... - - ==================================================================== - Common message format... - - Each message posted to the worker has an operation-independent - envelope and operation-dependent arguments: - - ``` - { - type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get' - - messageId: OPTIONAL arbitrary value. The worker will copy it as-is - into response messages to assist in client-side dispatching. - - dbId: a db identifier string (returned by 'open') which tells the - operation which database instance to work on. If not provided, the - first-opened db is used. This is an "opaque" value, with no - inherently useful syntax or information. Its value is subject to - change with any given build of this API and cannot be used as a - basis for anything useful beyond its one intended purpose. - - args: ...operation-dependent arguments... - - // the framework may add other properties for testing or debugging - // purposes. - - } - ``` - - Response messages, posted back to the main thread, look like: - - ``` - { - type: string. Same as above except for error responses, which have the type - 'error', - - messageId: same value, if any, provided by the inbound message - - dbId: the id of the db which was operated on, if any, as returned - by the corresponding 'open' operation. - - result: ...operation-dependent result... - - } - ``` - - ==================================================================== - Error responses - - Errors are reported messages in an operation-independent format: - - ``` - { - type: "error", - - messageId: ...as above..., - - dbId: ...as above... - - result: { - - operation: type of the triggering operation: 'open', 'close', ... - - message: ...error message text... - - errorClass: string. The ErrorClass.name property from the thrown exception. - - input: the message object which triggered the error. - - stack: _if available_, a stack trace array. - - } - - } - ``` - - - ==================================================================== - "config-get" - - This operation fetches the serializable parts of the sqlite3 API - configuration. - - Message format: - - ``` - { - type: "config-get", - messageId: ...as above..., - args: currently ignored and may be elided. - } - ``` - - Response: - - ``` - { - type: "config-get", - messageId: ...as above..., - result: { - - version: sqlite3.version object - - bigIntEnabled: bool. True if BigInt support is enabled. - - vfsList: result of sqlite3.capi.sqlite3_js_vfs_list() - } - } - ``` - - - ==================================================================== - "open" a database - - Message format: - - ``` - { - type: "open", - messageId: ...as above..., - args:{ - - filename [=":memory:" or "" (unspecified)]: the db filename. - See the sqlite3.oo1.DB constructor for peculiarities and - transformations, - - vfs: sqlite3_vfs name. Ignored if filename is ":memory:" or "". - This may change how the given filename is resolved. - } - } - ``` - - Response: - - ``` - { - type: "open", - messageId: ...as above..., - result: { - filename: db filename, possibly differing from the input. - - dbId: an opaque ID value which must be passed in the message - envelope to other calls in this API to tell them which db to - use. If it is not provided to future calls, they will default to - operating on the least-recently-opened db. This property is, for - API consistency's sake, also part of the containing message - envelope. Only the `open` operation includes it in the `result` - property. - - persistent: true if the given filename resides in the - known-persistent storage, else false. - - vfs: name of the VFS the "main" db is using. - } - } - ``` - - ==================================================================== - "close" a database - - Message format: - - ``` - { - type: "close", - messageId: ...as above... - dbId: ...as above... - args: OPTIONAL {unlink: boolean} - } - ``` - - If the `dbId` does not refer to an opened ID, this is a no-op. If - the `args` object contains a truthy `unlink` value then the database - will be unlinked (deleted) after closing it. The inability to close a - db (because it's not opened) or delete its file does not trigger an - error. - - Response: - - ``` - { - type: "close", - messageId: ...as above..., - result: { - - filename: filename of closed db, or undefined if no db was closed - - } - } - ``` - - ==================================================================== - "exec" SQL - - All SQL execution is processed through the exec operation. It offers - most of the features of the oo1.DB.exec() method, with a few limitations - imposed by the state having to cross thread boundaries. - - Message format: - - ``` - { - type: "exec", - messageId: ...as above... - dbId: ...as above... - args: string (SQL) or {... see below ...} - } - ``` - - Response: - - ``` - { - type: "exec", - messageId: ...as above..., - dbId: ...as above... - result: { - input arguments, possibly modified. See below. - } - } - ``` - - The arguments are in the same form accepted by oo1.DB.exec(), with - the exceptions noted below. - - If `args.countChanges` (added in version 3.43) is truthy then the - `result` property contained by the returned object will have a - `changeCount` property which holds the number of changes made by the - provided SQL. Because the SQL may contain an arbitrary number of - statements, the `changeCount` is calculated by calling - `sqlite3_total_changes()` before and after the SQL is evaluated. If - the value of `countChanges` is 64 then the `changeCount` property - will be returned as a 64-bit integer in the form of a BigInt (noting - that that will trigger an exception if used in a BigInt-incapable - build). In the latter case, the number of changes is calculated by - calling `sqlite3_total_changes64()` before and after the SQL is - evaluated. - - If the `args.lastInsertRowId` (added in version 3.50.0) is truthy - then the `result` property contained by the returned object will - have a `lastInsertRowId` will hold a BigInt-type value corresponding - to the result of sqlite3_last_insert_rowid(). This value is only - fetched once, after the SQL is run, regardless of how many - statements the SQL contains. This API has no idea whether the SQL - contains any INSERTs, so it is up to the client to apply/rely on - this property only when it makes sense to do so. - - A function-type args.callback property cannot cross - the window/Worker boundary, so is not useful here. If - args.callback is a string then it is assumed to be a - message type key, in which case a callback function will be - applied which posts each row result via: - - postMessage({type: thatKeyType, - rowNumber: 1-based-#, - row: theRow, - columnNames: anArray - }) - - And, at the end of the result set (whether or not any result rows - were produced), it will post an identical message with - (row=undefined, rowNumber=null) to alert the caller than the result - set is completed. Note that a row value of `null` is a legal row - result for certain arg.rowMode values. - - (Design note: we don't use (row=undefined, rowNumber=undefined) to - indicate end-of-results because fetching those would be - indistinguishable from fetching from an empty object unless the - client used hasOwnProperty() (or similar) to distinguish "missing - property" from "property with the undefined value". Similarly, - `null` is a legal value for `row` in some case , whereas the db - layer won't emit a result value of `undefined`.) - - The callback proxy must not recurse into this interface. An exec() - call will tie up the Worker thread, causing any recursion attempt - to wait until the first exec() is completed. - - The response is the input options object (or a synthesized one if - passed only a string), noting that options.resultRows and - options.columnNames may be populated by the call to db.exec(). - - - ==================================================================== - "export" the current db - - To export the underlying database as a byte array... - - Message format: - - ``` - { - type: "export", - messageId: ...as above..., - dbId: ...as above... - } - ``` - - Response: - - ``` - { - type: "export", - messageId: ...as above..., - dbId: ...as above... - result: { - byteArray: Uint8Array (as per sqlite3_js_db_export()), - filename: the db filename, - mimetype: "application/x-sqlite3" - } - } - ``` - - */ - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - const util = sqlite3.util; - sqlite3.initWorker1API = function() { - "use strict"; - const toss = (...args) => { - throw new Error(args.join(" ")); - }; - if (!(globalThis.WorkerGlobalScope instanceof Function)) toss("initWorker1API() must be run from a Worker thread."); - const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); - const DB = sqlite3.oo1.DB; - /** - Returns the app-wide unique ID for the given db, creating one if - needed. - */ - const getDbId = function(db) { - let id = wState.idMap.get(db); - if (id) return id; - id = "db#" + ++wState.idSeq + ":" + Math.floor(Math.random() * 1e8) + ":" + Math.floor(Math.random() * 1e8); - /** ^^^ can't simply use db.pointer b/c closing/opening may re-use - the same address, which could map pending messages to a wrong - instance. - - 2025-07: https://github.com/sqlite/sqlite-wasm/issues/113 - demonstrates that two Worker1s can end up with the same IDs, - despite using different instances of the library, so we need - to add some randomness to the IDs instead of relying on the - pointer addresses. - */ - wState.idMap.set(db, id); - return id; - }; - /** - Internal helper for managing Worker-level state. - */ - const wState = { - dbList: [], - idSeq: 0, - idMap: /* @__PURE__ */ new WeakMap(), - xfer: [], - open: function(opt) { - const db = new DB(opt); - this.dbs[getDbId(db)] = db; - if (this.dbList.indexOf(db) < 0) this.dbList.push(db); - return db; - }, - close: function(db, alsoUnlink) { - if (db) { - delete this.dbs[getDbId(db)]; - const filename = db.filename; - const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0); - db.close(); - const ddNdx = this.dbList.indexOf(db); - if (ddNdx >= 0) this.dbList.splice(ddNdx, 1); - if (alsoUnlink && filename && pVfs) util.sqlite3__wasm_vfs_unlink(pVfs, filename); - } - }, - post: function(msg, xferList) { - if (xferList && xferList.length) { - globalThis.postMessage(msg, Array.from(xferList)); - xferList.length = 0; - } else globalThis.postMessage(msg); - }, - dbs: Object.create(null), - getDb: function(id, require = true) { - return this.dbs[id] || (require ? toss("Unknown (or closed) DB ID:", id) : void 0); - } - }; - /** Throws if the given db is falsy or not opened, else returns its - argument. */ - const affirmDbOpen = function(db = wState.dbList[0]) { - return db && db.pointer ? db : toss("DB is not opened."); - }; - /** Extract dbId from the given message payload. */ - const getMsgDb = function(msgData, affirmExists = true) { - const db = wState.getDb(msgData.dbId, false) || wState.dbList[0]; - return affirmExists ? affirmDbOpen(db) : db; - }; - const getDefaultDbId = function() { - return wState.dbList[0] && getDbId(wState.dbList[0]); - }; - /** - A level of "organizational abstraction" for the Worker1 - API. Each method in this object must map directly to a Worker1 - message type key. The onmessage() dispatcher attempts to - dispatch all inbound messages to a method of this object, - passing it the event.data part of the inbound event object. All - methods must return a plain Object containing any result - state, which the dispatcher may amend. All methods must throw - on error. - */ - const wMsgHandler = { - open: function(ev) { - const oargs = Object.create(null), args = ev.args || Object.create(null); - if (args.simulateError) toss("Throwing because of simulateError flag."); - const rc = Object.create(null); - oargs.vfs = args.vfs; - oargs.filename = args.filename || ""; - const db = wState.open(oargs); - rc.filename = db.filename; - rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); - rc.dbId = getDbId(db); - rc.vfs = db.dbVfsName(); - return rc; - }, - close: function(ev) { - const db = getMsgDb(ev, false); - const response = { filename: db && db.filename }; - if (db) { - const doUnlink = ev.args && "object" === typeof ev.args ? !!ev.args.unlink : false; - wState.close(db, doUnlink); - } - return response; - }, - exec: function(ev) { - const rc = "string" === typeof ev.args ? { sql: ev.args } : ev.args || Object.create(null); - if ("stmt" === rc.rowMode) toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); - else if (!rc.sql) toss("'exec' requires input SQL."); - const db = getMsgDb(ev); - if (rc.callback || Array.isArray(rc.resultRows)) db._blobXfer = wState.xfer; - const theCallback = rc.callback; - let rowNumber = 0; - const hadColNames = !!rc.columnNames; - if ("string" === typeof theCallback) { - if (!hadColNames) rc.columnNames = []; - rc.callback = function(row, stmt) { - wState.post({ - type: theCallback, - columnNames: rc.columnNames, - rowNumber: ++rowNumber, - row - }, wState.xfer); - }; - } - try { - const changeCount = !!rc.countChanges ? db.changes(true, 64 === rc.countChanges) : void 0; - db.exec(rc); - if (void 0 !== changeCount) rc.changeCount = db.changes(true, 64 === rc.countChanges) - changeCount; - const lastInsertRowId = !!rc.lastInsertRowId ? sqlite3.capi.sqlite3_last_insert_rowid(db) : void 0; - if (void 0 !== lastInsertRowId) rc.lastInsertRowId = lastInsertRowId; - if (rc.callback instanceof Function) { - rc.callback = theCallback; - wState.post({ - type: theCallback, - columnNames: rc.columnNames, - rowNumber: null, - row: void 0 - }); - } - } finally { - delete db._blobXfer; - if (rc.callback) rc.callback = theCallback; - } - return rc; - }, - "config-get": function() { - const rc = Object.create(null), src = sqlite3.config; - ["bigIntEnabled"].forEach(function(k) { - if (Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; - }); - rc.version = sqlite3.version; - rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list(); - return rc; - }, - export: function(ev) { - const db = getMsgDb(ev); - const response = { - byteArray: sqlite3.capi.sqlite3_js_db_export(db.pointer), - filename: db.filename, - mimetype: "application/x-sqlite3" - }; - wState.xfer.push(response.byteArray.buffer); - return response; - }, - toss: function(ev) { - toss("Testing worker exception"); - } - }; - globalThis.onmessage = async function(ev) { - ev = ev.data; - let result, dbId = ev.dbId, evType = ev.type; - const arrivalTime = performance.now(); - try { - if (wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function) result = await wMsgHandler[evType](ev); - else toss("Unknown db worker message type:", ev.type); - } catch (err) { - evType = "error"; - result = { - operation: ev.type, - message: err.message, - errorClass: err.name, - input: ev - }; - if (err.stack) result.stack = "string" === typeof err.stack ? err.stack.split(/\n\s*/) : err.stack; - } - if (!dbId) dbId = result.dbId || getDefaultDbId(); - wState.post({ - type: evType, - dbId, - messageId: ev.messageId, - workerReceivedTime: arrivalTime, - workerRespondTime: performance.now(), - departureTime: ev.departureTime, - result - }, wState.xfer); - }; - globalThis.postMessage({ - type: "sqlite3-api", - result: "worker1-ready" - }); - }.bind({ sqlite3 }); - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; - const vfs = Object.create(null); - sqlite3.vfs = vfs; - /** - Uses sqlite3_vfs_register() to register this - sqlite3.capi.sqlite3_vfs instance. This object must have already - been filled out properly. If the first argument is truthy, the - VFS is registered as the default VFS, else it is not. - - On success, returns this object. Throws on error. - */ - capi.sqlite3_vfs.prototype.registerVfs = function(asDefault = false) { - if (!(this instanceof sqlite3.capi.sqlite3_vfs)) toss("Expecting a sqlite3_vfs-type argument."); - const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0); - if (rc) toss("sqlite3_vfs_register(", this, ") failed with rc", rc); - if (this.pointer !== capi.sqlite3_vfs_find(this.$zName)) toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", this); - return this; - }; - /** - A wrapper for - sqlite3.StructBinder.StructType.prototype.installMethods() or - registerVfs() to reduce installation of a VFS and/or its I/O - methods to a single call. - - Accepts an object which contains the properties "io" and/or - "vfs", each of which is itself an object with following properties: - - - `struct`: an sqlite3.StructBinder.StructType-type struct. This - must be a populated (except for the methods) object of type - sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the - "vfs" entry). - - - `methods`: an object mapping sqlite3_io_methods method names - (e.g. 'xClose') to JS implementations of those methods. The JS - implementations must be call-compatible with their native - counterparts. - - For each of those object, this function passes its (`struct`, - `methods`, (optional) `applyArgcCheck`) properties to - installMethods(). - - If the `vfs` entry is set then: - - - Its `struct` property's registerVfs() is called. The - `vfs` entry may optionally have an `asDefault` property, which - gets passed as the argument to registerVfs(). - - - If `struct.$zName` is falsy and the entry has a string-type - `name` property, `struct.$zName` is set to the C-string form of - that `name` value before registerVfs() is called. That string - gets added to the on-dispose state of the struct. - - On success returns this object. Throws on error. - */ - vfs.installVfs = function(opt) { - let count = 0; - const propList = ["io", "vfs"]; - for (const key of propList) { - const o = opt[key]; - if (o) { - ++count; - o.struct.installMethods(o.methods, !!o.applyArgcCheck); - if ("vfs" === key) { - if (!o.struct.$zName && "string" === typeof o.name) o.struct.addOnDispose(o.struct.$zName = wasm.allocCString(o.name)); - o.struct.registerVfs(!!o.asDefault); - } - } - } - if (!count) toss("Misuse: installVfs() options object requires at least", "one of:", propList); - return this; - }; - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - if (!sqlite3.wasm.exports.sqlite3_declare_vtab) return; - const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3; - const vtab = Object.create(null); - sqlite3.vtab = vtab; - const sii = capi.sqlite3_index_info; - /** - If n is >=0 and less than this.$nConstraint, this function - returns either a WASM pointer to the 0-based nth entry of - this.$aConstraint (if passed a truthy 2nd argument) or an - sqlite3_index_info.sqlite3_index_constraint object wrapping that - address (if passed a falsy value or no 2nd argument). Returns a - falsy value if n is out of range. - */ - sii.prototype.nthConstraint = function(n, asPtr = false) { - if (n < 0 || n >= this.$nConstraint) return false; - const ptr = wasm.ptr.add(this.$aConstraint, sii.sqlite3_index_constraint.structInfo.sizeof * n); - return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr); - }; - /** - Works identically to nthConstraint() but returns state from - this.$aConstraintUsage, so returns an - sqlite3_index_info.sqlite3_index_constraint_usage instance - if passed no 2nd argument or a falsy 2nd argument. - */ - sii.prototype.nthConstraintUsage = function(n, asPtr = false) { - if (n < 0 || n >= this.$nConstraint) return false; - const ptr = wasm.ptr.add(this.$aConstraintUsage, sii.sqlite3_index_constraint_usage.structInfo.sizeof * n); - return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr); - }; - /** - If n is >=0 and less than this.$nOrderBy, this function - returns either a WASM pointer to the 0-based nth entry of - this.$aOrderBy (if passed a truthy 2nd argument) or an - sqlite3_index_info.sqlite3_index_orderby object wrapping that - address (if passed a falsy value or no 2nd argument). Returns a - falsy value if n is out of range. - */ - sii.prototype.nthOrderBy = function(n, asPtr = false) { - if (n < 0 || n >= this.$nOrderBy) return false; - const ptr = wasm.ptr.add(this.$aOrderBy, sii.sqlite3_index_orderby.structInfo.sizeof * n); - return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr); - }; - /** - Internal factory function for xVtab and xCursor impls. - */ - const __xWrapFactory = function(methodName, StructType) { - return function(ptr, removeMapping = false) { - if (0 === arguments.length) ptr = new StructType(); - if (ptr instanceof StructType) { - this.set(ptr.pointer, ptr); - return ptr; - } else if (!wasm.isPtr(ptr)) sqlite3.SQLite3Error.toss("Invalid argument to", methodName + "()"); - let rc = this.get(ptr); - if (removeMapping) this.delete(ptr); - return rc; - }.bind(/* @__PURE__ */ new Map()); - }; - /** - A factory function which implements a simple lifetime manager for - mappings between C struct pointers and their JS-level wrappers. - The first argument must be the logical name of the manager - (e.g. 'xVtab' or 'xCursor'), which is only used for error - reporting. The second must be the capi.XYZ struct-type value, - e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor. - - Returns an object with 4 methods: create(), get(), unget(), and - dispose(), plus a StructType member with the value of the 2nd - argument. The methods are documented in the body of this - function. - */ - const StructPtrMapper = function(name, StructType) { - const __xWrap = __xWrapFactory(name, StructType); - /** - This object houses a small API for managing mappings of (`T*`) - to StructType objects, specifically within the lifetime - requirements of sqlite3_module methods. - */ - return Object.assign(Object.create(null), { - StructType, - create: (ppOut) => { - const rc = __xWrap(); - wasm.pokePtr(ppOut, rc.pointer); - return rc; - }, - get: (pCObj) => __xWrap(pCObj), - unget: (pCObj) => __xWrap(pCObj, true), - dispose: (pCObj) => __xWrap(pCObj, true)?.dispose?.() - }); - }; - /** - A lifetime-management object for mapping `sqlite3_vtab*` - instances in sqlite3_module methods to capi.sqlite3_vtab - objects. - - The API docs are in the API-internal StructPtrMapper(). - */ - vtab.xVtab = StructPtrMapper("xVtab", capi.sqlite3_vtab); - /** - A lifetime-management object for mapping `sqlite3_vtab_cursor*` - instances in sqlite3_module methods to capi.sqlite3_vtab_cursor - objects. - - The API docs are in the API-internal StructPtrMapper(). - */ - vtab.xCursor = StructPtrMapper("xCursor", capi.sqlite3_vtab_cursor); - /** - Convenience form of creating an sqlite3_index_info wrapper, - intended for use in xBestIndex implementations. Note that the - caller is expected to call dispose() on the returned object - before returning. Though not _strictly_ required, as that object - does not own the pIdxInfo memory, it is nonetheless good form. - */ - vtab.xIndexInfo = (pIdxInfo) => new capi.sqlite3_index_info(pIdxInfo); - /** - Given an sqlite3_module method name and error object, this - function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof - sqlite3.WasmAllocError), else it returns its second argument. Its - intended usage is in the methods of a sqlite3_vfs or - sqlite3_module: - - ``` - try{ - let rc = ... - return rc; - }catch(e){ - return sqlite3.vtab.xError( - 'xColumn', e, sqlite3.capi.SQLITE_XYZ); - // where SQLITE_XYZ is some call-appropriate result code. - } - ``` - - If no 3rd argument is provided, its default depends on - the error type: - - - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM. - - - If err is an SQLite3Error then its `resultCode` property - is used. - - - If all else fails, capi.SQLITE_ERROR is used. - - If xError.errorReporter is a function, it is called in - order to report the error, else the error is not reported. - If that function throws, that exception is ignored. - */ - vtab.xError = function f(methodName, err, defaultRc) { - if (f.errorReporter instanceof Function) try { - f.errorReporter("sqlite3_module::" + methodName + "(): " + err.message); - } catch (e) {} - let rc; - if (err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM; - else if (arguments.length > 2) rc = defaultRc; - else if (err instanceof sqlite3.SQLite3Error) rc = err.resultCode; - return rc || capi.SQLITE_ERROR; - }; - vtab.xError.errorReporter = sqlite3.config.error.bind(sqlite3.config); - /** - A helper for sqlite3_vtab::xRowid() and xUpdate() - implementations. It must be passed the final argument to one of - those methods (an output pointer to an int64 row ID) and the - value to store at the output pointer's address. Returns the same - as wasm.poke() and will throw if the 1st or 2nd arguments - are invalid for that function. - - Example xRowid impl: - - ``` - const xRowid = (pCursor, ppRowid64)=>{ - const c = vtab.xCursor(pCursor); - vtab.xRowid(ppRowid64, c.myRowId); - return 0; - }; - ``` - */ - vtab.xRowid = (ppRowid64, value) => wasm.poke(ppRowid64, value, "i64"); - /** - A helper to initialize and set up an sqlite3_module object for - later installation into individual databases using - sqlite3_create_module(). Requires an object with the following - properties: - - - `methods`: an object containing a mapping of properties with - the C-side names of the sqlite3_module methods, e.g. xCreate, - xBestIndex, etc., to JS implementations for those functions. - Certain special-case handling is performed, as described below. - - - `catchExceptions` (default=false): if truthy, the given methods - are not mapped as-is, but are instead wrapped inside wrappers - which translate exceptions into result codes of SQLITE_ERROR or - SQLITE_NOMEM, depending on whether the exception is an - sqlite3.WasmAllocError. In the case of the xConnect and xCreate - methods, the exception handler also sets the output error - string to the exception's error string. - - - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If - not set, one will be created automatically. If the current - "this" is-a sqlite3_module then it is unconditionally used in - place of `struct`. - - - OPTIONAL `iVersion`: if set, it must be an integer value and it - gets assigned to the `$iVersion` member of the struct object. - If it's _not_ set, and the passed-in `struct` object's `$iVersion` - is 0 (the default) then this function attempts to define a value - for that property based on the list of methods it has. - - If `catchExceptions` is false, it is up to the client to ensure - that no exceptions escape the methods, as doing so would move - them through the C API, leading to undefined - behavior. (vtab.xError() is intended to assist in reporting - such exceptions.) - - Certain methods may refer to the same implementation. To simplify - the definition of such methods: - - - If `methods.xConnect` is `true` then the value of - `methods.xCreate` is used in its place, and vice versa. sqlite - treats xConnect/xCreate functions specially if they are exactly - the same function (same pointer value). - - - If `methods.xDisconnect` is true then the value of - `methods.xDestroy` is used in its place, and vice versa. - - This is to facilitate creation of those methods inline in the - passed-in object without requiring the client to explicitly get a - reference to one of them in order to assign it to the other - one. - - The `catchExceptions`-installed handlers will account for - identical references to the above functions and will install the - same wrapper function for both. - - The given methods are expected to return integer values, as - expected by the C API. If `catchExceptions` is truthy, the return - value of the wrapped function will be used as-is and will be - translated to 0 if the function returns a falsy value (e.g. if it - does not have an explicit return). If `catchExceptions` is _not_ - active, the method implementations must explicitly return integer - values. - - Throws on error. On success, returns the sqlite3_module object - (`this` or `opt.struct` or a new sqlite3_module instance, - depending on how it's called). - */ - vtab.setupModule = function(opt) { - let createdMod = false; - const mod = this instanceof capi.sqlite3_module ? this : opt.struct || (createdMod = new capi.sqlite3_module()); - try { - const methods = opt.methods || toss("Missing 'methods' object."); - for (const e of Object.entries({ - xConnect: "xCreate", - xDisconnect: "xDestroy" - })) { - const k = e[0], v = e[1]; - if (true === methods[k]) methods[k] = methods[v]; - else if (true === methods[v]) methods[v] = methods[k]; - } - if (opt.catchExceptions) { - const fwrap = function(methodName, func) { - if (["xConnect", "xCreate"].indexOf(methodName) >= 0) return function(pDb, pAux, argc, argv, ppVtab, pzErr) { - try { - return func(...arguments) || 0; - } catch (e) { - if (!(e instanceof sqlite3.WasmAllocError)) { - wasm.dealloc(wasm.peekPtr(pzErr)); - wasm.pokePtr(pzErr, wasm.allocCString(e.message)); - } - return vtab.xError(methodName, e); - } - }; - else return function(...args) { - try { - return func(...args) || 0; - } catch (e) { - return vtab.xError(methodName, e); - } - }; - }; - const mnames = [ - "xCreate", - "xConnect", - "xBestIndex", - "xDisconnect", - "xDestroy", - "xOpen", - "xClose", - "xFilter", - "xNext", - "xEof", - "xColumn", - "xRowid", - "xUpdate", - "xBegin", - "xSync", - "xCommit", - "xRollback", - "xFindFunction", - "xRename", - "xSavepoint", - "xRelease", - "xRollbackTo", - "xShadowName" - ]; - const remethods = Object.create(null); - for (const k of mnames) { - const m = methods[k]; - if (!(m instanceof Function)) continue; - else if ("xConnect" === k && methods.xCreate === m) remethods[k] = methods.xCreate; - else if ("xCreate" === k && methods.xConnect === m) remethods[k] = methods.xConnect; - else remethods[k] = fwrap(k, m); - } - mod.installMethods(remethods, false); - } else mod.installMethods(methods, !!opt.applyArgcCheck); - if (0 === mod.$iVersion) { - let v; - if ("number" === typeof opt.iVersion) v = opt.iVersion; - else if (mod.$xIntegrity) v = 4; - else if (mod.$xShadowName) v = 3; - else if (mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2; - else v = 1; - mod.$iVersion = v; - } - } catch (e) { - if (createdMod) createdMod.dispose(); - throw e; - } - return mod; - }; - /** - Equivalent to calling vtab.setupModule() with this sqlite3_module - object as the call's `this`. - */ - capi.sqlite3_module.prototype.setupModule = function(opt) { - return vtab.setupModule.call(this, opt); - }; - }); - /** - kvvfs - the Key/Value VFS - is an SQLite3 VFS which delegates - storage of its pages and metadata to a key-value store. - - It was conceived in order to support JS's localStorage and - sessionStorage objects. Its native implementation uses files as - key/value storage (one file per record) but the JS implementation - replaces a few methods so that it can use the aforementioned - objects as storage. - - It uses a bespoke ASCII encoding to store each db page as a - separate record and stores some metadata, like the db's unencoded - size and its journal, as individual records. - - kvvfs is significantly less efficient than a plain in-memory db but - it also, as a side effect of its design, offers a JSON-friendly - interchange format for exporting and importing databases. - - kvvfs is _not_ designed for heavy db loads. It is relatively - malloc()-heavy, having to de/allocate frequently, and it - spends much of its time converting the raw db pages into and out of - an ASCII encoding. - - But it _does_ work and is "performant enough" for db work of the - scale of a db which will fit within sessionStorage or localStorage - (just 2-3mb). - - "Version 2" extends it to support using Storage-like objects as - backing storage, Storage being the JS class which localStorage and - sessionStorage both derive from. This essentially moves the backing - store from whatever localStorage and sessionStorage use to an - in-memory object. - - This effort is primarily a stepping stone towards eliminating, if - it proves possible, the POSIX I/O API dependencies in SQLite's WASM - builds. That is: if this VFS works properly, it can be set as the - default VFS and we can eliminate the "unix" VFS from the JS/WASM - builds (as opposed to server-wise/WASI builds). That still, as of - 2025-11-23, a ways away, but it's the main driver for version 2 of - kvvfs. - - Version 2 remains compatible with version 1 databases and always - writes localStorage/sessionStorage metadata in the v1 format, so - such dbs can be manipulated freely by either version. For transient - storage objects (new in version 2), the format of its record keys - is simpified, requiring less space than v1 keys by eliding - redundant (in this context) info from the keys. - - Another benefit of v2 is its ability to export dbs into a - JSON-friendly (but not human-friendly) format. - - A potential, as-yet-unproven, benefit, would be the ability to plug - arbitrary Storage-compatible objects in so that clients could, - e.g. asynchronously post updates to db pages to some back-end for - backups. - */ - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - "use strict"; - const capi = sqlite3.capi, sqlite3_kvvfs_methods = capi.sqlite3_kvvfs_methods, KVVfsFile = capi.KVVfsFile, pKvvfs = sqlite3.capi.sqlite3_vfs_find("kvvfs"); - delete capi.sqlite3_kvvfs_methods; - delete capi.KVVfsFile; - if (!pKvvfs) return; - const util = sqlite3.util, wasm = sqlite3.wasm, toss3 = util.toss3, hop = (o, k) => Object.prototype.hasOwnProperty.call(o, k); - const kvvfsMethods = new sqlite3_kvvfs_methods(wasm.exports.sqlite3__wasm_kvvfs_methods()); - util.assert(32 <= kvvfsMethods.$nKeySize, "unexpected kvvfsMethods.$nKeySize: " + kvvfsMethods.$nKeySize); - /** - Most of the VFS-internal state. - */ - const cache = Object.assign(Object.create(null), { - rxJournalSuffix: /-journal$/, - zKeyJrnl: wasm.allocCString("jrnl"), - zKeySz: wasm.allocCString("sz"), - keySize: kvvfsMethods.$nKeySize, - buffer: Object.assign(Object.create(null), { - n: kvvfsMethods.$nBufferSize, - pool: Object.create(null) - }) - }); - /** - Returns a (cached) wasm.alloc()'d buffer of cache.buffer.n size, - throwing on OOM. - - We leak this one-time alloc because we've no better option. - sqlite3_vfs does not have a finalizer, so we've no place to hook - in the cleanup. We "could" extend sqlite3_shutdown() to have a - cleanup list for stuff like this but that function is never - used in JS, so it's hardly worth it. - */ - cache.memBuffer = (id = 0) => cache.buffer.pool[id] ??= wasm.alloc(cache.buffer.n); - /** Frees the buffer with the given id. */ - cache.memBufferFree = (id) => { - const b = cache.buffer.pool[id]; - if (b) { - wasm.dealloc(b); - delete cache.buffer.pool[id]; - } - }; - const noop = () => {}; - const debug = sqlite3.__isUnderTest ? (...args) => sqlite3.config.debug("kvvfs:", ...args) : noop; - const warn = (...args) => sqlite3.config.warn("kvvfs:", ...args); - const error = (...args) => sqlite3.config.error("kvvfs:", ...args); - /** - Implementation of JS's Storage interface for use as backing store - of the kvvfs. Storage is a native class and its constructor - cannot be legally called from JS, making it impossible to - directly subclass Storage. This class implements (only) the - Storage interface, to make it a drop-in replacement for - localStorage/sessionStorage. (Any behavioral discrepancies are to - be considered bugs.) - - This impl simply proxies a plain, prototype-less Object, suitable - for JSON-ing. - - Design note: Storage has a bit of an odd iteration-related - interface as does not (AFAIK) specify specific behavior regarding - modification during traversal. Because of that, this class does - some seemingly unnecessary things with its #keys member, deleting - and recreating it whenever a property index might be invalidated. - */ - class KVVfsStorage { - #map; - #keys; - #getKeys() { - return this.#keys ??= Object.keys(this.#map); - } - constructor() { - this.clear(); - } - key(n) { - const k = this.#getKeys(); - return n < k.length ? k[n] : null; - } - getItem(k) { - return this.#map[k] ?? null; - } - setItem(k, v) { - if (!hop(this.#map, k)) this.#keys = null; - this.#map[k] = "" + v; - } - removeItem(k) { - if (delete this.#map[k]) this.#keys = null; - } - clear() { - this.#map = Object.create(null); - this.#keys = null; - } - get length() { - return this.#getKeys().length; - } - } - /** True if v is the name of one of the special persistant Storage - objects. */ - const kvvfsIsPersistentName = (v) => "local" === v || "session" === v; - /** - Keys in kvvfs have a prefix of "kvvfs-NAME-", where NAME is the - db name. This key is redundant in JS but it's how kvvfs works (it - saves each key to a separate file, so needs a distinct namespace - per data source name). We retain this prefix in 'local' and - 'session' storage for backwards compatibility and so that they - can co-exist with client data in their storage, but we elide them - from "v2" storage, where they're superfluous. - */ - const kvvfsKeyPrefix = (v) => kvvfsIsPersistentName(v) ? "kvvfs-" + v + "-" : ""; - /** - Throws if storage name n (JS string) is not valid for use as a - storage name. Much of this goes back to kvvfs having a fixed - buffer size for its keys, and the storage name needing to be - encoded in the keys for local/session storage. - - The second argument must only be true when called from xOpen() - - it makes names with a "-journal" suffix legal. - */ - const validateStorageName = function(n, mayBeJournal = false) { - if (kvvfsIsPersistentName(n)) return; - const len = new Blob([n]).size; - if (!len) toss3(capi.SQLITE_MISUSE, "Empty name is not permitted."); - let maxLen = cache.keySize - 1; - if (cache.rxJournalSuffix.test(n)) { - if (!mayBeJournal) toss3(capi.SQLITE_MISUSE, "Storage names may not have a '-journal' suffix."); - } else if (["-wal", "-shm"].filter((v) => n.endsWith(v)).length) toss3(capi.SQLITE_MISUSE, "Storage names may not have a -wal or -shm suffix."); - else maxLen -= 8; - if (len > maxLen) toss3(capi.SQLITE_RANGE, "Storage name is too long. Limit =", maxLen); - let i; - for (i = 0; i < len; ++i) { - const ch = n.codePointAt(i); - if (ch < 32) toss3(capi.SQLITE_RANGE, "Illegal character (" + ch + "d) in storage name:", n); - } - }; - /** - Create a new instance of the objects which go into - cache.storagePool, with a refcount of 1. If passed a Storage-like - object as its second argument, it is used for the storage, - otherwise it creates a new KVVfsStorage object. - */ - const newStorageObj = (name, storage = void 0) => Object.assign(Object.create(null), { - jzClass: name, - refc: 1, - deleteAtRefc0: false, - storage: storage || new KVVfsStorage(), - keyPrefix: kvvfsKeyPrefix(name), - files: [], - listeners: void 0 - }); - /** - Public interface for kvvfs v2. The capi.sqlite3_js_kvvfs_...() - routines remain in place for v1. Some members of this class proxy - to those functions but use different default argument values in - some cases. - */ - const kvvfs = sqlite3.kvvfs = Object.create(null); - if (sqlite3.__isUnderTest) kvvfs.log = Object.assign(Object.create(null), { - xOpen: false, - xClose: false, - xWrite: false, - xRead: false, - xSync: false, - xAccess: false, - xFileControl: false, - xRcrdRead: false, - xRcrdWrite: false, - xRcrdDelete: false - }); - /** - Deletes the cache.storagePool entries for store (a - cache.storagePool entry) and its db/journal counterpart. - */ - const deleteStorage = function(store) { - const other = cache.rxJournalSuffix.test(store.jzClass) ? store.jzClass.replace(cache.rxJournalSuffix, "") : store.jzClass + "-journal"; - kvvfs?.log?.xClose && debug("cleaning up storage handles [", store.jzClass, other, "]", store); - delete cache.storagePool[store.jzClass]; - delete cache.storagePool[other]; - if (!sqlite3.__isUnderTest) { - delete store.storage; - delete store.refc; - } - }; - /** - Add both store.jzClass and store.jzClass+"-journal" - to cache,storagePool. - */ - const installStorageAndJournal = (store) => cache.storagePool[store.jzClass] = cache.storagePool[store.jzClass + "-journal"] = store; - /** - The public name of the current thread's transient storage - object. A storage object with this name gets preinstalled. - */ - const nameOfThisThreadStorage = "."; - /** - Map of JS-stringified KVVfsFile::zClass names to - reference-counted Storage objects. These objects are created in - xOpen(). Their refcount is decremented in xClose(), and the - record is destroyed if the refcount reaches 0. We refcount so - that concurrent active xOpen()s on a given name, and within a - given thread, use the same storage object. - */ - cache.storagePool = Object.assign(Object.create(null), { [nameOfThisThreadStorage]: newStorageObj(nameOfThisThreadStorage) }); - if (globalThis.Storage) { - if (globalThis.localStorage instanceof globalThis.Storage) cache.storagePool.local = newStorageObj("local", globalThis.localStorage); - if (globalThis.sessionStorage instanceof globalThis.Storage) cache.storagePool.session = newStorageObj("session", globalThis.sessionStorage); - } - cache.builtinStorageNames = Object.keys(cache.storagePool); - const isBuiltinName = (n) => cache.builtinStorageNames.indexOf(n) > -1; - for (const k of Object.keys(cache.storagePool)) { - const orig = cache.storagePool[k]; - cache.storagePool[k + "-journal"] = orig; - } - cache.setError = (e = void 0, dfltErrCode = capi.SQLITE_ERROR) => { - if (e) { - cache.lastError = e; - return e.resultCode | 0 || dfltErrCode; - } - delete cache.lastError; - return 0; - }; - cache.popError = () => { - const e = cache.lastError; - delete cache.lastError; - return e; - }; - /** Exception handler for notifyListeners(). */ - const catchForNotify = (e) => { - warn("kvvfs.listener handler threw:", e); - }; - const kvvfsDecode = wasm.exports.sqlite3__wasm_kvvfs_decode; - const kvvfsEncode = wasm.exports.sqlite3__wasm_kvvfs_encode; - /** - Listener events and their argument(s) (via the callback(ev) - ev.data member): - - 'open': number of opened handles on this storage. - - 'close': number of opened handles on this storage. - - 'write': key, value - - 'delete': key - - 'sync': true if it's from xSync(), false if it's from - xFileControl(). - - For efficiency's sake, all calls to this function should - be in the form: - - store.listeners && notifyListeners(...); - - Failing to do so will trigger an exceptin in this function (which - will be ignored but may produce a console warning). - */ - const notifyListeners = async function(eventName, store, ...args) { - try { - if (store.keyPrefix && args[0]) args[0] = args[0].replace(store.keyPrefix, ""); - let u8enc, z0, z1, wcache; - for (const ear of store.listeners) { - const ev = Object.create(null); - ev.storageName = store.jzClass; - ev.type = eventName; - ear.decodePages; - const f = ear.events[eventName]; - if (f) { - if (!ear.includeJournal && args[0] === "jrnl") continue; - if ("write" === eventName && ear.decodePages && +args[0] > 0) { - ev.data = [args[0]]; - if (wcache?.[args[0]]) { - ev.data[1] = wcache[args[0]]; - continue; - } - u8enc ??= new TextEncoder("utf-8"); - z0 ??= cache.memBuffer(10); - z1 ??= cache.memBuffer(11); - const u = u8enc.encode(args[1]); - const heap = wasm.heap8u(); - heap.set(u, Number(z0)); - heap[wasm.ptr.addn(z0, u.length)] = 0; - const rc = kvvfsDecode(z0, z1, cache.buffer.n); - if (rc > 0) { - wcache ??= Object.create(null); - wcache[args[0]] = ev.data[1] = heap.slice(Number(z1), wasm.ptr.addn(z1, rc)); - } else continue; - } else ev.data = args.length ? args.length === 1 ? args[0] : args : void 0; - try { - f(ev)?.catch?.(catchForNotify); - } catch (e) { - warn("notifyListeners [", store.jzClass, "]", eventName, e); - } - } - } - } catch (e) { - catchForNotify(e); - } - }; - /** - Returns the storage object mapped to the given string zClass - (C-string pointer or JS string). - */ - const storageForZClass = (zClass) => "string" === typeof zClass ? cache.storagePool[zClass] : cache.storagePool[wasm.cstrToJs(zClass)]; - const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKey; - /** - Returns a C string from kvvfsMakeKey() OR returns zKey. In the - former case the memory is static, so must be copied before a - second call. zKey MUST be a pointer passed to a VFS/file method, - to allow us to avoid an alloc and/or an snprintf(). It requires - C-string arguments for zClass and zKey. zClass may be NULL but - zKey may not. - */ - const zKeyForStorage = (store, zClass, zKey) => { - return zClass && store.keyPrefix ? kvvfsMakeKey(zClass, zKey) : zKey; - }; - const jsKeyForStorage = (store, zClass, zKey) => wasm.cstrToJs(zKeyForStorage(store, zClass, zKey)); - const storageGetDbSize = (store) => +store.storage.getItem(store.keyPrefix + "sz"); - /** - sqlite3_file pointers => objects, each of which has: - - .file = KVVfsFile instance - - .jzClass = JS-string form of f.$zClass - - .storage = Storage object. It is shared between a db and its - journal. - */ - const pFileHandles = /* @__PURE__ */ new Map(); - /** - Original WASM functions for methods we partially override. - */ - const originalMethods = { - vfs: Object.create(null), - ioDb: Object.create(null), - ioJrnl: Object.create(null) - }; - const pVfs = new capi.sqlite3_vfs(kvvfsMethods.$pVfs); - const pIoDb = new capi.sqlite3_io_methods(kvvfsMethods.$pIoDb); - const pIoJrnl = new capi.sqlite3_io_methods(kvvfsMethods.$pIoJrnl); - const recordHandler = Object.create(null); - const kvvfsInternal = Object.assign(Object.create(null), { - pFileHandles, - cache, - storageForZClass, - KVVfsStorage, - disablePageSizeChange: true - }); - if (kvvfs.log) kvvfs.internal = kvvfsInternal; - /** - Implementations for members of the object referred to by - sqlite3__wasm_kvvfs_methods(). We swap out some native - implementations with these so that we can use JS Storage for - their backing store. - */ - const methodOverrides = { - recordHandler: { - xRcrdRead: (zClass, zKey, zBuf, nBuf) => { - try { - const jzClass = wasm.cstrToJs(zClass); - const store = storageForZClass(jzClass); - if (!store) return -1; - const jXKey = jsKeyForStorage(store, zClass, zKey); - kvvfs?.log?.xRcrdRead && warn("xRcrdRead", jzClass, jXKey, nBuf, store); - const jV = store.storage.getItem(jXKey); - if (null === jV) return -1; - const nV = jV.length; - if (nBuf <= 0) return nV; - else if (1 === nBuf) { - wasm.poke(zBuf, 0); - return nV; - } - if (nBuf + 1 < nV) toss3(capi.SQLITE_RANGE, "xRcrdRead()", jzClass, jXKey, "input buffer is too small: need", nV, "but have", nBuf); - const zV = cache.memBuffer(0); - const heap = wasm.heap8(); - let i; - for (i = 0; i < nV; ++i) heap[wasm.ptr.add(zV, i)] = jV.codePointAt(i) & 255; - heap.copyWithin(Number(zBuf), Number(zV), wasm.ptr.addn(zV, i)); - heap[wasm.ptr.add(zBuf, nV)] = 0; - return nBuf; - } catch (e) { - error("kvrecordRead()", e); - cache.setError(e); - return -2; - } - }, - xRcrdWrite: (zClass, zKey, zData) => { - try { - const store = storageForZClass(zClass); - const jxKey = jsKeyForStorage(store, zClass, zKey); - const jData = wasm.cstrToJs(zData); - kvvfs?.log?.xRcrdWrite && warn("xRcrdWrite", jxKey, store); - store.storage.setItem(jxKey, jData); - store.listeners && notifyListeners("write", store, jxKey, jData); - return 0; - } catch (e) { - error("kvrecordWrite()", e); - return cache.setError(e, capi.SQLITE_IOERR); - } - }, - xRcrdDelete: (zClass, zKey) => { - try { - const store = storageForZClass(zClass); - const jxKey = jsKeyForStorage(store, zClass, zKey); - kvvfs?.log?.xRcrdDelete && warn("xRcrdDelete", jxKey, store); - store.storage.removeItem(jxKey); - store.listeners && notifyListeners("delete", store, jxKey); - return 0; - } catch (e) { - error("kvrecordDelete()", e); - return cache.setError(e, capi.SQLITE_IOERR); - } - } - }, - vfs: { - xOpen: function(pProtoVfs, zName, pProtoFile, flags, pOutFlags) { - cache.popError(); - let zToFree; - try { - if (!zName) { - zToFree = wasm.allocCString("" + pProtoFile + "." + (Math.random() * 1e5 | 0)); - zName = zToFree; - } - const jzClass = wasm.cstrToJs(zName); - kvvfs?.log?.xOpen && debug("xOpen", jzClass, "flags =", flags); - validateStorageName(jzClass, true); - if (flags & (capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_TEMP_DB | capi.SQLITE_OPEN_TRANSIENT_DB) && cache.rxJournalSuffix.test(jzClass)) toss3(capi.SQLITE_ERROR, "DB files may not have a '-journal' suffix."); - let s = storageForZClass(jzClass); - if (!s && !(flags & capi.SQLITE_OPEN_CREATE)) toss3(capi.SQLITE_ERROR, "Storage not found:", jzClass); - const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile, flags, pOutFlags); - if (rc) return rc; - let deleteAt0 = !!(capi.SQLITE_OPEN_DELETEONCLOSE & flags); - if (wasm.isPtr(arguments[1])) { - if (capi.sqlite3_uri_boolean(zName, "delete-on-close", 0)) deleteAt0 = true; - } - const f = new KVVfsFile(pProtoFile); - util.assert(f.$zClass, "Missing f.$zClass"); - f.addOnDispose(zToFree); - zToFree = void 0; - if (s) { - ++s.refc; - s.files.push(f); - wasm.poke32(pOutFlags, flags); - } else { - wasm.poke32(pOutFlags, flags | capi.SQLITE_OPEN_CREATE); - util.assert(!f.$isJournal, "Opening a journal before its db? " + jzClass); - const nm = jzClass.replace(cache.rxJournalSuffix, ""); - s = newStorageObj(nm); - installStorageAndJournal(s); - s.files.push(f); - s.deleteAtRefc0 = deleteAt0; - kvvfs?.log?.xOpen && debug("xOpen installed storage handle [", nm, nm + "-journal", "]", s); - } - pFileHandles.set(pProtoFile, { - store: s, - file: f, - jzClass - }); - s.listeners && notifyListeners("open", s, s.files.length); - return 0; - } catch (e) { - warn("xOpen:", e); - return cache.setError(e); - } finally { - zToFree && wasm.dealloc(zToFree); - } - }, - xDelete: function(pVfs, zName, iSyncFlag) { - cache.popError(); - try { - const jzName = wasm.cstrToJs(zName); - if (cache.rxJournalSuffix.test(jzName)) recordHandler.xRcrdDelete(zName, cache.zKeyJrnl); - return 0; - } catch (e) { - warn("xDelete", e); - return cache.setError(e); - } - }, - xAccess: function(pProtoVfs, zPath, flags, pResOut) { - cache.popError(); - try { - const s = storageForZClass(zPath); - const jzPath = s?.jzClass || wasm.cstrToJs(zPath); - if (kvvfs?.log?.xAccess) debug("xAccess", jzPath, "flags =", flags, "*pResOut =", wasm.peek32(pResOut), "store =", s); - if (!s) - /** The xAccess method returns [SQLITE_OK] on success or some - ** non-zero error code if there is an I/O error or if the name of - ** the file given in the second argument is illegal. - */ - try { - validateStorageName(jzPath); - } catch (e) { - wasm.poke32(pResOut, 0); - return 0; - } - if (s) { - const key = s.keyPrefix + (cache.rxJournalSuffix.test(jzPath) ? "jrnl" : "1"); - const res = s.storage.getItem(key) ? 0 : 1; - wasm.poke32(pResOut, res); - } else wasm.poke32(pResOut, 0); - return 0; - } catch (e) { - error("xAccess", e); - return cache.setError(e); - } - }, - xRandomness: function(pVfs, nOut, pOut) { - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; - return nOut; - }, - xGetLastError: function(pVfs, nOut, pOut) { - const e = cache.popError(); - debug("xGetLastError", e); - if (e) { - const scope = wasm.scopedAllocPush(); - try { - const [cMsg, n] = wasm.scopedAllocCString(e.message, true); - wasm.cstrncpy(pOut, cMsg, nOut); - if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); - debug("set xGetLastError", e.message); - return e.resultCode | 0 || capi.SQLITE_IOERR; - } catch (e) { - return capi.SQLITE_NOMEM; - } finally { - wasm.scopedAllocPop(scope); - } - } - return 0; - } - }, - ioDb: { - xClose: function(pFile) { - cache.popError(); - try { - const h = pFileHandles.get(pFile); - kvvfs?.log?.xClose && debug("xClose", pFile, h); - if (h) { - pFileHandles.delete(pFile); - const s = h.store; - s.files = s.files.filter((v) => v !== h.file); - if (--s.refc <= 0 && s.deleteAtRefc0) deleteStorage(s); - originalMethods.ioDb.xClose(pFile); - h.file.dispose(); - s.listeners && notifyListeners("close", s, s.files.length); - } - return 0; - } catch (e) { - error("xClose", e); - return cache.setError(e); - } - }, - xFileControl: function(pFile, opId, pArg) { - cache.popError(); - try { - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - kvvfs?.log?.xFileControl && debug("xFileControl", h, "op =", opId); - if (opId === capi.SQLITE_FCNTL_PRAGMA && kvvfsInternal.disablePageSizeChange) { - const zName = wasm.peekPtr(wasm.ptr.add(pArg, wasm.ptr.size)); - if ("page_size" === wasm.cstrToJs(zName)) { - kvvfs?.log?.xFileControl && debug("xFileControl pragma", wasm.cstrToJs(zName)); - const zVal = wasm.peekPtr(wasm.ptr.add(pArg, 2 * wasm.ptr.size)); - if (zVal) { - kvvfs?.log?.xFileControl && warn("xFileControl pragma", h, "NOT setting page size to", wasm.cstrToJs(zVal)); - h.file.$szPage = -1; - return 0; - } else if (h.file.$szPage > 0) { - kvvfs?.log?.xFileControl && warn("xFileControl", h, "getting page size", h.file.$szPage); - wasm.pokePtr(pArg, wasm.allocCString("" + h.file.$szPage)); - return 0; - } - } - } - const rc = originalMethods.ioDb.xFileControl(pFile, opId, pArg); - if (0 == rc && capi.SQLITE_FCNTL_SYNC === opId) h.store.listeners && notifyListeners("sync", h.store, false); - return rc; - } catch (e) { - error("xFileControl", e); - return cache.setError(e); - } - }, - xSync: function(pFile, flags) { - cache.popError(); - try { - const h = pFileHandles.get(pFile); - kvvfs?.log?.xSync && debug("xSync", h); - util.assert(h, "Missing KVVfsFile handle"); - const rc = originalMethods.ioDb.xSync(pFile, flags); - if (0 == rc && h.store.listeners) notifyListeners("sync", h.store, true); - return rc; - } catch (e) { - error("xSync", e); - return cache.setError(e); - } - }, - xRead: function(pFile, pTgt, n, iOff64) { - cache.popError(); - try { - if (kvvfs?.log?.xRead) { - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xRead", n, iOff64, h); - } - return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); - } catch (e) { - error("xRead", e); - return cache.setError(e); - } - }, - xWrite: function(pFile, pSrc, n, iOff64) { - cache.popError(); - try { - if (kvvfs?.log?.xWrite) { - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xWrite", n, iOff64, h); - } - return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); - } catch (e) { - error("xWrite", e); - return cache.setError(e); - } - } - }, - ioJrnl: { xClose: true } - }; - debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, kvvfsMethods, capi.sqlite3_file.structInfo, KVVfsFile.structInfo); - try { - util.assert(cache.buffer.n > 1024 * 129, "Heap buffer is not large enough"); - for (const e of Object.entries(methodOverrides.recordHandler)) { - const k = e[0], f = e[1]; - recordHandler[k] = f; - kvvfsMethods[kvvfsMethods.memberKey(k)] = wasm.installFunction(kvvfsMethods.memberSignature(k), f); - } - for (const e of Object.entries(methodOverrides.vfs)) { - const k = e[0], f = e[1], km = pVfs.memberKey(k), member = pVfs.structInfo.members[k] || util.toss("Missing pVfs.structInfo[", k, "]"); - originalMethods.vfs[k] = wasm.functionEntry(pVfs[km]); - pVfs[km] = wasm.installFunction(member.signature, f); - } - for (const e of Object.entries(methodOverrides.ioDb)) { - const k = e[0], f = e[1], km = pIoDb.memberKey(k); - originalMethods.ioDb[k] = wasm.functionEntry(pIoDb[km]) || util.toss("Missing native pIoDb[", km, "]"); - pIoDb[km] = wasm.installFunction(pIoDb.memberSignature(k), f); - } - for (const e of Object.entries(methodOverrides.ioJrnl)) { - const k = e[0], f = e[1], km = pIoJrnl.memberKey(k); - originalMethods.ioJrnl[k] = wasm.functionEntry(pIoJrnl[km]) || util.toss("Missing native pIoJrnl[", km, "]"); - if (true === f) pIoJrnl[km] = pIoDb[km] || util.toss("Missing copied pIoDb[", km, "]"); - else pIoJrnl[km] = wasm.installFunction(pIoJrnl.memberSignature(k), f); - } - } finally { - kvvfsMethods.dispose(); - pVfs.dispose(); - pIoDb.dispose(); - pIoJrnl.dispose(); - } - /** - Clears all storage used by the kvvfs DB backend, deleting any - DB(s) stored there. - - Its argument must be the name of a kvvfs storage object: - - - 'session' - - 'local' - - '' - see below. - - A transient kvvfs storage object name. - - In the first two cases, only sessionStorage resp. localStorage is - cleared. An empty string resolves to both 'local' and 'session' - storage. - - Returns the number of entries cleared. - - As of kvvfs version 2: - - This API is available in Worker threads but does not have access - to localStorage or sessionStorage in them. Prior versions did not - include this API in Worker threads. - - Differences in this function in version 2: - - - It accepts an arbitrary storage name. In v1 this was a silent - no-op for any names other than ('local','session',''). - - - It throws if a db currently has the storage opened UNLESS the - storage object is localStorage or sessionStorage. That version 1 - did not throw for this case was due to an architectural - limitation which has since been overcome, but removal of - JsStorageDb.prototype.clearStorage() would be a backwards compatibility - break, so this function permits wiping the storage for those two - cases even if they are opened. Use with case. - */ - const sqlite3_js_kvvfs_clear = function callee(which) { - if ("" === which) return callee("local") + callee("session"); - const store = storageForZClass(which); - if (!store) return 0; - if (store.files.length) if (globalThis.localStorage === store.storage || globalThis.sessionStorage === store.storage) {} else toss3(capi.SQLITE_ACCESS, "Cannot clear in-use database storage."); - const s = store.storage; - const toRm = []; - let i, n = s.length; - for (i = 0; i < n; ++i) { - const k = s.key(i); - if (!store.keyPrefix || k.startsWith(store.keyPrefix)) toRm.push(k); - } - toRm.forEach((kk) => s.removeItem(kk)); - return toRm.length; - }; - /** - This routine estimates the approximate amount of - storage used by the given kvvfs back-end. - - Its arguments are as documented for sqlite3_js_kvvfs_clear(), - only the operation this performs is different. - - The returned value is twice the "length" value of every matching - key and value, noting that JavaScript stores each character in 2 - bytes. - - The returned size is not authoritative from the perspective of - how much data can fit into localStorage and sessionStorage, as - the precise algorithms for determining those limits are - unspecified and may include per-entry overhead invisible to - clients. - */ - const sqlite3_js_kvvfs_size = function callee(which) { - if ("" === which) return callee("local") + callee("session"); - const store = storageForZClass(which); - if (!store) return 0; - const s = store.storage; - let i, sz = 0; - for (i = 0; i < s.length; ++i) { - const k = s.key(i); - if (!store.keyPrefix || k.startsWith(store.keyPrefix)) { - sz += k.length; - sz += s.getItem(k).length; - } - } - return sz * 2; - }; - /** - Exports a kvvfs storage object to an object, optionally - JSON-friendly. - - Usages: - - thisfunc(storageName); - thisfunc(options); - - In the latter case, the options object must be an object with - the following properties: - - - "name" (string) required. The storage to export. - - - "decodePages" (bool=false). If true, the .pages result property - holdes Uint8Array objects holding the raw binary-format db - pages. The default is to use kvvfs-encoded string pages - (JSON-friendly). - - - "includeJournal" (bool=false). If true and the db has a current - journal, it is exported as well. (Kvvfs journals are stored as a - single record within the db's storage object.) - - The returned object is structured as follows... - - - "name": the name of the storage. This is 'local' or 'session' - for localStorage resp. sessionStorage, and an arbitrary name for - transient storage. This propery may be changed before passing - this object to sqlite3_js_kvvfs_import() in order to - import into a different storage object. - - - "timestamp": the time this function was called, in Unix - epoch milliseconds. - - - "size": the unencoded db size. - - - "journal": if options.includeJournal is true and this db has a - journal, it is stored as a string here, otherwise this property - is not set. - - - "pages": An array holding the raw encoded db pages in their - proper order. - - Throws if this db is not opened. - - The encoding of the underlying database is not part of this - interface - it is simply passed on as-is. Interested parties are - directed to src/os_kv.c in the SQLite source tree, with the - caveat that that code also does not offer a public interface. - i.e. the encoding is a private implementation detail of kvvfs. - The format may be changed in the future but kvvfs will continue - to support the current form. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_export = function callee(...args) { - let opt; - if (1 === args.length && "object" === typeof args[0]) opt = args[0]; - else if (args.length) opt = Object.assign(Object.create(null), { name: args[0] }); - const store = opt ? storageForZClass(opt.name) : null; - if (!store) toss3(capi.SQLITE_NOTFOUND, "There is no kvvfs storage named", opt?.name); - const s = store.storage; - const rc = Object.assign(Object.create(null), { - name: store.jzClass, - timestamp: Date.now(), - pages: [] - }); - const pages = Object.create(null); - const keyPrefix = store.keyPrefix; - const rxTail = keyPrefix ? /^kvvfs-[^-]+-(\w+)/ : void 0; - let i = 0, n = s.length; - for (; i < n; ++i) { - const k = s.key(i); - if (!keyPrefix || k.startsWith(keyPrefix)) { - let kk = (keyPrefix ? rxTail.exec(k) : void 0)?.[1] ?? k; - switch (kk) { - case "jrnl": - if (opt.includeJournal) rc.journal = s.getItem(k); - break; - case "sz": - rc.size = +s.getItem(k); - break; - default: - kk = +kk; - if (!util.isInt32(kk) || kk <= 0) toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: " + k); - if (opt.decodePages) { - const spg = s.getItem(k), n = spg.length, z = cache.memBuffer(0), zDec = cache.memBuffer(1), heap = wasm.heap8u(); - let i = 0; - for (; i < n; ++i) heap[wasm.ptr.add(z, i)] = spg.codePointAt(i) & 255; - heap[wasm.ptr.add(z, i)] = 0; - const nDec = kvvfsDecode(z, zDec, cache.buffer.n); - pages[kk] = heap.slice(Number(zDec), wasm.ptr.addn(zDec, nDec)); - } else pages[kk] = s.getItem(k); - break; - } - } - } - if (opt.decodePages) cache.memBufferFree(1); - Object.keys(pages).map((v) => +v).sort().forEach((v) => rc.pages.push(pages[v])); - return rc; - }; - /** - The counterpart of sqlite3_js_kvvfs_export(). Its - argument must be the result of that function() or - a compatible one. - - This either replaces the contents of an existing transient - storage object or installs one named exp.name, setting - the storage's db contents to that of the exp object. - - Throws on error. Error conditions include: - - - The given storage object is currently opened by any db. - Performing this page-by-page import would invoke undefined - behavior on them. - - - Malformed input object. - - If it throws after starting the import then it clears the storage - before returning, to avoid leaving the db in an undefined - state. It may throw for any of the above-listed conditions before - reaching that step, in which case the db is not modified. If - exp.name refers to a new storage name then if it throws, the name - does not get installed. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_import = function(exp, overwrite = false) { - if (!exp?.timestamp || !exp.name || void 0 === exp.size || !Array.isArray(exp.pages)) toss3(capi.SQLITE_MISUSE, "Malformed export object."); - else if (!exp.size || exp.size !== (exp.size | 0) || exp.size >= 2147483647) toss3(capi.SQLITE_RANGE, "Invalid db size: " + exp.size); - validateStorageName(exp.name); - let store = storageForZClass(exp.name); - const isNew = !store; - if (store) { - if (!overwrite) toss3(capi.SQLITE_ACCESS, "Storage '" + exp.name + "' already exists and", "overwrite was not specified."); - else if (!store.files || !store.jzClass) toss3(capi.SQLITE_ERROR, "Internal storage object", exp.name, "seems to be malformed."); - else if (store.files.length) toss3(capi.SQLITE_IOERR_ACCESS, "Cannot import db storage while it is in use."); - sqlite3_js_kvvfs_clear(exp.name); - } else store = newStorageObj(exp.name); - const keyPrefix = kvvfsKeyPrefix(exp.name); - let zEnc; - try { - const s = store.storage; - s.setItem(keyPrefix + "sz", exp.size); - if (exp.journal) s.setItem(keyPrefix + "jrnl", exp.journal); - if (exp.pages[0] instanceof Uint8Array) exp.pages.forEach((u, ndx) => { - const n = u.length; - zEnc ??= cache.memBuffer(1); - const zBin = cache.memBuffer(0), heap = wasm.heap8u(); - heap.set(u, Number(zBin)); - heap[wasm.ptr.addn(zBin, n)] = 0; - const rc = kvvfsEncode(zBin, n, zEnc); - util.assert(rc < cache.buffer.n, "Impossibly long output - possibly smashed the heap"); - util.assert(0 === wasm.peek8(wasm.ptr.add(zEnc, rc)), "Expecting NUL-terminated encoded output"); - const jenc = wasm.cstrToJs(zEnc); - s.setItem(keyPrefix + (ndx + 1), jenc); - }); - else if (exp.pages[0]) exp.pages.forEach((v, ndx) => s.setItem(keyPrefix + (ndx + 1), v)); - if (isNew) installStorageAndJournal(store); - } catch { - if (!isNew) try { - sqlite3_js_kvvfs_clear(exp.name); - } catch (ee) {} - } finally { - if (zEnc) cache.memBufferFree(1); - } - return this; - }; - /** - If no kvvfs storage exists with the given name, one is - installed. If one exists, its reference count is increased so - that it won't be freed by the closing of a database or journal - file. - - Throws if the name is not valid for a new storage object. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_reserve = function(name) { - let store = storageForZClass(name); - if (store) { - ++store.refc; - return; - } - validateStorageName(name); - installStorageAndJournal(newStorageObj(name)); - }; - /** - Conditionally "unlinks" a kvvfs storage object, reducing its - reference count by 1. - - This is a no-op if name ends in "-journal" or refers to a - built-in storage object. - - It will not lower the refcount below the number of - currently-opened db/journal files for the storage (so that it - cannot delete it out from under them). - - If the refcount reaches 0 then the storage object is - removed. - - Returns true if it reduces the refcount, else false. A result of - true does not necessarily mean that the storage unit was removed, - just that its refcount was lowered. Similarly, a result of false - does not mean that the storage is removed - it may still have - opened handles. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_unlink = function(name) { - const store = storageForZClass(name); - if (!store || kvvfsIsPersistentName(store.jzClass) || isBuiltinName(store.jzClass) || cache.rxJournalSuffix.test(name)) return false; - if (store.refc > store.files.length || 0 === store.files.length) { - if (--store.refc <= 0) deleteStorage(store); - return true; - } - return false; - }; - /** - Adds an event listener to a kvvfs storage object. The idea is - that this can be used to asynchronously back up one kvvfs storage - object to another or another channel entirely. (The caveat in the - latter case is that kvvfs's format is not readily consumable by - downstream code.) - - Its argument must be an object with the following properties: - - - storage: the name of the kvvfs storage object. - - - reserve [=false]: if true, sqlite3_js_kvvfs_reserve() is used - to ensure that the storage exists if it does not already. - If this is false and the storage does not exist then an - exception is thrown. - - - events: an object which may have any of the following - callback function properties: open, close, write, delete. - - - decodePages [=false]: if true, write events will receive each - db page write in the form of a Uint8Array holding the raw binary - db page. The default is to emit the kvvfs-format page because it - requires no extra work, we already have it in hand, and it's - often smaller. It's not great for interchange, though. - - - includeJournal [=false]: if true, writes and deletes of - "jrnl" records are included. If false, no events are sent - for journal updates. - - Passing the same object to sqlite3_js_kvvfs_unlisten() will - remove the listener. - - Each one of the events callbacks will be called asynchronously - when the given storage performs those operations. They may be - asynchronous functions but are not required to be (the events are - fired async either way, but making the event callbacks async may - be advantageous when multiple listeners are involved). All - exceptions, including those via Promises, are ignored but may (or - may not) trigger warning output on the console. - - Each callback gets passed a single object with the following - properties: - - .type = the same as the name of the callback - - .storageName = the name of the storage object - - .data = callback-dependent: - - - 'open' and 'close' get an integer, the number of - currently-opened handles on the storage. - - - 'write' gets a length-two array holding the key and value which - were written. The key is always a string, even if it's a db page - number. For db-page records, the value's type depends on - opt.decodePages. All others, including the journal, are strings. - (The journal, being a kvvfs-specific format, is delivered in - that same JSON-friendly format.) More details below. - - - 'delete' gets the string-type key of the deleted record. - - - 'sync' gets a boolean value: true if it was triggered by db - file's xSync(), false if it was triggered by xFileControl(). The - latter triggers before the xSync() and also triggers if the DB - has PRAGMA SYNCHRONOUS=OFF (in which case xSync() is not - triggered). - - The key/value arguments to 'write', and key argument to 'delete', - are in one of the following forms: - - - 'sz' = the unencoded db size as a string. This specific key is - key is never deleted, so is only ever passed to 'write' events. - - - 'jrnl' = the current db journal as a kvvfs-encoded string. This - journal format is not useful anywhere except in the kvvfs - internals. These events are not fired if opt.includeJournal is - false. - - - '[1-9][0-9]*' (a db page number) = Its type depends on - opt.decodePages. These may be written and deleted in arbitrary - order. - - Design note: JS has StorageEvents but only in the main thread, - which is why the listeners are not based on that. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_listen = function(opt) { - if (!opt || "object" !== typeof opt) toss3(capi.SQLITE_MISUSE, "Expecting a listener object."); - let store = storageForZClass(opt.storage); - if (!store) if (opt.storage && opt.reserve) { - sqlite3_js_kvvfs_reserve(opt.storage); - store = storageForZClass(opt.storage); - util.assert(store, "Unexpectedly cannot fetch reserved storage " + opt.storage); - } else toss3(capi.SQLITE_NOTFOUND, "No such storage:", opt.storage); - if (opt.events) (store.listeners ??= []).push(opt); - }; - /** - Removes the kvvfs event listeners for the given options - object. It must be passed the same object instance which was - passed to sqlite3_js_kvvfs_listen(). - - This has no side effects if opt is invalid or is not a match for - any listeners. - - Return true if it unregisters its argument, else false. - - Added in version 3.52.0. - */ - const sqlite3_js_kvvfs_unlisten = function(opt) { - const store = storageForZClass(opt?.storage); - if (store?.listeners && opt.events) { - const n = store.listeners.length; - store.listeners = store.listeners.filter((v) => v !== opt); - const rc = n > store.listeners.length; - if (!store.listeners.length) store.listeners = void 0; - return rc; - } - return false; - }; - sqlite3.kvvfs.reserve = sqlite3_js_kvvfs_reserve; - sqlite3.kvvfs.import = sqlite3_js_kvvfs_import; - sqlite3.kvvfs.export = sqlite3_js_kvvfs_export; - sqlite3.kvvfs.unlink = sqlite3_js_kvvfs_unlink; - sqlite3.kvvfs.listen = sqlite3_js_kvvfs_listen; - sqlite3.kvvfs.unlisten = sqlite3_js_kvvfs_unlisten; - sqlite3.kvvfs.exists = (name) => !!storageForZClass(name); - sqlite3.kvvfs.estimateSize = sqlite3_js_kvvfs_size; - sqlite3.kvvfs.clear = sqlite3_js_kvvfs_clear; - if (globalThis.Storage) { - /** - Prior to version 2, kvvfs was only available in the main - thread. We retain that for the v1 APIs, exposing them only in - the main UI thread. As of version 2, kvvfs is available in all - threads but only via its v2 interface (sqlite3.kvvfs). - - These versions have a default argument value of "" which the v2 - versions lack. - */ - capi.sqlite3_js_kvvfs_size = (which = "") => sqlite3_js_kvvfs_size(which); - capi.sqlite3_js_kvvfs_clear = (which = "") => sqlite3_js_kvvfs_clear(which); - } - if (sqlite3.oo1?.DB) { - /** - Functionally equivalent to DB(storageName,'c','kvvfs') except - that it throws if the given storage name is not one of 'local' - or 'session'. - - As of version 3.46, the argument may optionally be an options - object in the form: - - { - filename: 'session'|'local', - ... etc. (all options supported by the DB ctor) - } - - noting that the 'vfs' option supported by main DB - constructor is ignored here: the vfs is always 'kvvfs'. - */ - const DB = sqlite3.oo1.DB; - sqlite3.oo1.JsStorageDb = function(storageName = sqlite3.oo1.JsStorageDb.defaultStorageName) { - const opt = DB.dbCtorHelper.normalizeArgs(...arguments); - opt.vfs = "kvvfs"; - switch (opt.filename) { - case ":sessionStorage:": - opt.filename = "session"; - break; - case ":localStorage:": - opt.filename = "local"; - break; - } - const m = /(file:(\/\/)?)([^?]+)/.exec(opt.filename); - validateStorageName(m ? m[3] : opt.filename); - DB.dbCtorHelper.call(this, opt); - }; - sqlite3.oo1.JsStorageDb.defaultStorageName = cache.storagePool.session ? "session" : nameOfThisThreadStorage; - const jdb = sqlite3.oo1.JsStorageDb; - jdb.prototype = Object.create(DB.prototype); - jdb.clearStorage = sqlite3_js_kvvfs_clear; - /** - DEPRECATED: the inherited method of this name (as opposed to - the "static" class method) is deprecated with version 2 of - kvvfs. This function will, for backwards comaptibility, - continue to work with localStorage and sessionStorage, but will - throw for all other storage because they are opened. Version 1 - was not capable of recognizing that the storage was opened so - permitted wiping it out at any time, but that was arguably a - bug. - - Clears this database instance's storage or throws if this - instance has been closed. Returns the number of - database pages which were cleaned up. - */ - jdb.prototype.clearStorage = function() { - return jdb.clearStorage(this.affirmOpen().dbFilename(), true); - }; - /** Equivalent to sqlite3_js_kvvfs_size(). */ - jdb.storageSize = sqlite3_js_kvvfs_size; - /** - Returns the _approximate_ number of bytes this database takes - up in its storage or throws if this instance has been closed. - */ - jdb.prototype.storageSize = function() { - return jdb.storageSize(this.affirmOpen().dbFilename(), true); - }; - } - if (sqlite3.__isUnderTest && sqlite3.vtab) { - /** - An eponymous vtab for inspecting the kvvfs state. This is only - intended for use in testing and development, not part of the - public API. - */ - const cols = Object.assign(Object.create(null), { - rowid: { type: "INTEGER" }, - name: { type: "TEXT" }, - nRef: { type: "INTEGER" }, - nOpen: { type: "INTEGER" }, - isTransient: { type: "INTEGER" }, - dbSize: { type: "INTEGER" } - }); - Object.keys(cols).forEach((v, i) => cols[v].colId = i); - const VT = sqlite3.vtab; - const ProtoCursor = Object.assign(Object.create(null), { row: function() { - return cache.storagePool[this.names[this.rowid]]; - } }); - Object.assign(Object.create(ProtoCursor), { - rowid: 0, - names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) - }); - const cursorState = function(cursor, reset) { - const o = cursor instanceof capi.sqlite3_vtab_cursor ? cursor : VT.xCursor.get(cursor); - if (reset || !o.vTabState) o.vTabState = Object.assign(Object.create(ProtoCursor), { - rowid: 0, - names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v)) - }); - return o.vTabState; - }; - const dbg = () => {}; - const theModule = function f() { - return f.mod ??= new sqlite3.capi.sqlite3_module().setupModule({ - catchExceptions: true, - methods: { - xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr) { - dbg("xConnect"); - try { - const xcol = []; - Object.keys(cols).forEach((k) => { - xcol.push(k + " " + cols[k].type); - }); - const rc = capi.sqlite3_declare_vtab(pDb, "CREATE TABLE ignored(" + xcol.join(",") + ")"); - if (0 === rc) { - const t = VT.xVtab.create(ppVtab); - util.assert(t === VT.xVtab.get(wasm.peekPtr(ppVtab)), "output pointer check failed"); - } - return rc; - } catch (e) { - return VT.xErrror("xConnect", e, capi.SQLITE_ERROR); - } - }, - xCreate: wasm.ptr.null, - xDisconnect: function(pVtab) { - dbg("xDisconnect", ...arguments); - VT.xVtab.dispose(pVtab); - return 0; - }, - xOpen: function(pVtab, ppCursor) { - dbg("xOpen", ...arguments); - VT.xCursor.create(ppCursor); - return 0; - }, - xClose: function(pCursor) { - dbg("xClose", ...arguments); - const c = VT.xCursor.unget(pCursor); - delete c.vTabState; - c.dispose(); - return 0; - }, - xNext: function(pCursor) { - dbg("xNext", ...arguments); - const c = VT.xCursor.get(pCursor); - ++cursorState(c).rowid; - return 0; - }, - xColumn: function(pCursor, pCtx, iCol) { - dbg("xColumn", ...arguments); - const st = cursorState(pCursor); - const store = st.row(); - util.assert(store, "Unexpected xColumn call"); - switch (iCol) { - case cols.rowid.colId: - capi.sqlite3_result_int(pCtx, st.rowid); - break; - case cols.name.colId: - capi.sqlite3_result_text(pCtx, store.jzClass, -1, capi.SQLITE_TRANSIENT); - break; - case cols.nRef.colId: - capi.sqlite3_result_int(pCtx, store.refc); - break; - case cols.nOpen.colId: - capi.sqlite3_result_int(pCtx, store.files.length); - break; - case cols.isTransient.colId: - capi.sqlite3_result_int(pCtx, !!store.deleteAtRefc0); - break; - case cols.dbSize.colId: - capi.sqlite3_result_int(pCtx, storageGetDbSize(store)); - break; - default: - capi.sqlite3_result_error(pCtx, "Invalid column id: " + iCol); - return capi.SQLITE_RANGE; - } - return 0; - }, - xRowid: function(pCursor, ppRowid64) { - dbg("xRowid", ...arguments); - const st = cursorState(pCursor); - VT.xRowid(ppRowid64, st.rowid); - return 0; - }, - xEof: function(pCursor) { - const st = cursorState(pCursor); - dbg("xEof?=" + !st.row(), ...arguments); - return !st.row(); - }, - xFilter: function(pCursor, idxNum, idxCStr, argc, argv) { - dbg("xFilter", ...arguments); - cursorState(pCursor, true); - return 0; - }, - xBestIndex: function(pVtab, pIdxInfo) { - dbg("xBestIndex", ...arguments); - const pii = new capi.sqlite3_index_info(pIdxInfo); - pii.$estimatedRows = cache.storagePool.size; - pii.$estimatedCost = 1; - pii.dispose(); - return 0; - } - } - }); - }; - sqlite3.kvvfs.create_module = function(pDb, name = "sqlite_kvvfs") { - return capi.sqlite3_create_module(pDb, name, theModule(), wasm.ptr.null); - }; - } - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - /** - installOpfsVfs() returns a Promise which, on success, installs an - sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs - which accept a VFS. It is intended to be called via - sqlite3ApiBootstrap.initializers or an equivalent mechanism. - - The installed VFS uses the Origin-Private FileSystem API for - all file storage. On error it is rejected with an exception - explaining the problem. Reasons for rejection include, but are - not limited to: - - - The counterpart Worker (see below) could not be loaded. - - - The environment does not support OPFS. That includes when - this function is called from the main window thread. - - Significant notes and limitations: - - - The OPFS features used here are only available in dedicated Worker - threads. This file tries to detect that case, resulting in a - rejected Promise if those features do not seem to be available. - - - It requires the SharedArrayBuffer and Atomics classes, and the - former is only available if the HTTP server emits the so-called - COOP and COEP response headers. These features are required for - proxying OPFS's synchronous API via the synchronous interface - required by the sqlite3_vfs API. - - - This function may only be called a single time. When called, this - function removes itself from the sqlite3 object. - - All arguments to this function are for internal/development purposes - only. They do not constitute a public API and may change at any - time. - - The argument may optionally be a plain object with the following - configuration options: - - - proxyUri: name of the async proxy JS file. - - - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables - logging of errors. 2 enables logging of warnings and errors. 3 - additionally enables debugging info. Logging is performed - via the sqlite3.config.{log|warn|error}() functions. - - - sanityChecks (=false): if true, some basic sanity tests are run on - the OPFS VFS API after it's initialized, before the returned - Promise resolves. This is only intended for testing and - development of the VFS, not client-side use. - - On success, the Promise resolves to the top-most sqlite3 namespace - object and that object gets a new object installed in its - `opfs` property, containing several OPFS-specific utilities. - */ - const installOpfsVfs = function callee(options) { - if (!globalThis.SharedArrayBuffer || !globalThis.Atomics) return Promise.reject(/* @__PURE__ */ new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. The server must emit the COOP/COEP response headers to enable those. See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep")); - else if ("undefined" === typeof WorkerGlobalScope) return Promise.reject(/* @__PURE__ */ new Error("The OPFS sqlite3_vfs cannot run in the main thread because it requires Atomics.wait().")); - else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); - if (!options || "object" !== typeof options) options = Object.create(null); - const urlParams = new URL(globalThis.location.href).searchParams; - if (urlParams.has("opfs-disable")) return Promise.resolve(sqlite3); - if (void 0 === options.verbose) options.verbose = urlParams.has("opfs-verbose") ? +urlParams.get("opfs-verbose") || 2 : 1; - if (void 0 === options.sanityChecks) options.sanityChecks = urlParams.has("opfs-sanity-check"); - if (void 0 === options.proxyUri) options.proxyUri = callee.defaultProxyUri; - if ("function" === typeof options.proxyUri) options.proxyUri = options.proxyUri(); - return new Promise(function(promiseResolve_, promiseReject_) { - const loggers = [ - sqlite3.config.error, - sqlite3.config.warn, - sqlite3.config.log - ]; - const logImpl = (level, ...args) => { - if (options.verbose > level) loggers[level]("OPFS syncer:", ...args); - }; - const log = (...args) => logImpl(2, ...args); - const warn = (...args) => logImpl(1, ...args); - const error = (...args) => logImpl(0, ...args); - const toss = sqlite3.util.toss; - const capi = sqlite3.capi; - const util = sqlite3.util; - const wasm = sqlite3.wasm; - const sqlite3_vfs = capi.sqlite3_vfs; - const sqlite3_file = capi.sqlite3_file; - const sqlite3_io_methods = capi.sqlite3_io_methods; - /** - Generic utilities for working with OPFS. This will get filled out - by the Promise setup and, on success, installed as sqlite3.opfs. - - ACHTUNG: do not rely on these APIs in client code. They are - experimental and subject to change or removal as the - OPFS-specific sqlite3_vfs evolves. - */ - const opfsUtil = Object.create(null); - /** - Returns true if _this_ thread has access to the OPFS APIs. - */ - const thisThreadHasOPFS = () => { - return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory; - }; - /** - Not part of the public API. Solely for internal/development - use. - */ - opfsUtil.metrics = { - dump: function() { - let k, n = 0, t = 0, w = 0; - for (k in state.opIds) { - const m = metrics[k]; - n += m.count; - t += m.time; - w += m.wait; - m.avgTime = m.count && m.time ? m.time / m.count : 0; - m.avgWait = m.count && m.wait ? m.wait / m.count : 0; - } - sqlite3.config.log(globalThis.location.href, "metrics for", globalThis.location.href, ":", metrics, "\nTotal of", n, "op(s) for", t, "ms (incl. " + w + " ms of waiting on the async side)"); - sqlite3.config.log("Serialization metrics:", metrics.s11n); - W.postMessage({ type: "opfs-async-metrics" }); - }, - reset: function() { - let k; - const r = (m) => m.count = m.time = m.wait = 0; - for (k in state.opIds) r(metrics[k] = Object.create(null)); - let s = metrics.s11n = Object.create(null); - s = s.serialize = Object.create(null); - s.count = s.time = 0; - s = metrics.s11n.deserialize = Object.create(null); - s.count = s.time = 0; - } - }; - const opfsIoMethods = new sqlite3_io_methods(); - const opfsVfs = new sqlite3_vfs().addOnDispose(() => opfsIoMethods.dispose()); - let promiseWasRejected = void 0; - const promiseReject = (err) => { - promiseWasRejected = true; - opfsVfs.dispose(); - return promiseReject_(err); - }; - const promiseResolve = () => { - promiseWasRejected = false; - return promiseResolve_(sqlite3); - }; - const W = new Worker(new URL(options.proxyUri, import.meta.url)); - setTimeout(() => { - if (void 0 === promiseWasRejected) promiseReject(/* @__PURE__ */ new Error("Timeout while waiting for OPFS async proxy worker.")); - }, 4e3); - W._originalOnError = W.onerror; - W.onerror = function(err) { - error("Error initializing OPFS asyncer:", err); - promiseReject(/* @__PURE__ */ new Error("Loading OPFS async Worker failed for unknown reasons.")); - }; - const pDVfs = capi.sqlite3_vfs_find(null); - const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : null; - opfsIoMethods.$iVersion = 1; - opfsVfs.$iVersion = 2; - opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - opfsVfs.$mxPathname = 1024; - opfsVfs.$zName = wasm.allocCString("opfs"); - opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; - opfsVfs.addOnDispose("$zName", opfsVfs.$zName, "cleanup default VFS wrapper", () => dVfs ? dVfs.dispose() : null); - /** - Pedantic sidebar about opfsVfs.ondispose: the entries in that array - are items to clean up when opfsVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the opfsVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - /** - State which we send to the async-api Worker or share with it. - This object must initially contain only cloneable or sharable - objects. After the worker's "inited" message arrives, other types - of data may be added to it. - - For purposes of Atomics.wait() and Atomics.notify(), we use a - SharedArrayBuffer with one slot reserved for each of the API - proxy's methods. The sync side of the API uses Atomics.wait() - on the corresponding slot and the async side uses - Atomics.notify() on that slot. - - The approach of using a single SAB to serialize comms for all - instances might(?) lead to deadlock situations in multi-db - cases. We should probably have one SAB here with a single slot - for locking a per-file initialization step and then allocate a - separate SAB like the above one for each file. That will - require a bit of acrobatics but should be feasible. The most - problematic part is that xOpen() would have to use - postMessage() to communicate its SharedArrayBuffer, and mixing - that approach with Atomics.wait/notify() gets a bit messy. - */ - const state = Object.create(null); - state.verbose = options.verbose; - state.littleEndian = (() => { - const buffer = /* @__PURE__ */ new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true); - return new Int16Array(buffer)[0] === 256; - })(); - /** - asyncIdleWaitTime is how long (ms) to wait, in the async proxy, - for each Atomics.wait() when waiting on inbound VFS API calls. - We need to wake up periodically to give the thread a chance to - do other things. If this is too high (e.g. 500ms) then even two - workers/tabs can easily run into locking errors. Some multiple - of this value is also used for determining how long to wait on - lock contention to free up. - */ - state.asyncIdleWaitTime = 150; - /** - Whether the async counterpart should log exceptions to - the serialization channel. That produces a great deal of - noise for seemingly innocuous things like xAccess() checks - for missing files, so this option may have one of 3 values: - - 0 = no exception logging. - - 1 = only log exceptions for "significant" ops like xOpen(), - xRead(), and xWrite(). - - 2 = log all exceptions. - */ - state.asyncS11nExceptions = 1; - state.fileBufferSize = 1024 * 64; - state.sabS11nOffset = state.fileBufferSize; - /** - The size of the block in our SAB for serializing arguments and - result values. Needs to be large enough to hold serialized - values of any of the proxied APIs. Filenames are the largest - part but are limited to opfsVfs.$mxPathname bytes. We also - store exceptions there, so it needs to be long enough to hold - a reasonably long exception string. - */ - state.sabS11nSize = opfsVfs.$mxPathname * 2; - /** - The SAB used for all data I/O between the synchronous and - async halves (file i/o and arg/result s11n). - */ - state.sabIO = new SharedArrayBuffer(state.fileBufferSize + state.sabS11nSize); - state.opIds = Object.create(null); - const metrics = Object.create(null); - { - let i = 0; - state.opIds.whichOp = i++; - state.opIds.rc = i++; - state.opIds.xAccess = i++; - state.opIds.xClose = i++; - state.opIds.xDelete = i++; - state.opIds.xDeleteNoWait = i++; - state.opIds.xFileSize = i++; - state.opIds.xLock = i++; - state.opIds.xOpen = i++; - state.opIds.xRead = i++; - state.opIds.xSleep = i++; - state.opIds.xSync = i++; - state.opIds.xTruncate = i++; - state.opIds.xUnlock = i++; - state.opIds.xWrite = i++; - state.opIds.mkdir = i++; - state.opIds["opfs-async-metrics"] = i++; - state.opIds["opfs-async-shutdown"] = i++; - state.opIds.retry = i++; - state.sabOP = new SharedArrayBuffer(i * 4); - opfsUtil.metrics.reset(); - } - /** - SQLITE_xxx constants to export to the async worker - counterpart... - */ - state.sq3Codes = Object.create(null); - [ - "SQLITE_ACCESS_EXISTS", - "SQLITE_ACCESS_READWRITE", - "SQLITE_BUSY", - "SQLITE_CANTOPEN", - "SQLITE_ERROR", - "SQLITE_IOERR", - "SQLITE_IOERR_ACCESS", - "SQLITE_IOERR_CLOSE", - "SQLITE_IOERR_DELETE", - "SQLITE_IOERR_FSYNC", - "SQLITE_IOERR_LOCK", - "SQLITE_IOERR_READ", - "SQLITE_IOERR_SHORT_READ", - "SQLITE_IOERR_TRUNCATE", - "SQLITE_IOERR_UNLOCK", - "SQLITE_IOERR_WRITE", - "SQLITE_LOCK_EXCLUSIVE", - "SQLITE_LOCK_NONE", - "SQLITE_LOCK_PENDING", - "SQLITE_LOCK_RESERVED", - "SQLITE_LOCK_SHARED", - "SQLITE_LOCKED", - "SQLITE_MISUSE", - "SQLITE_NOTFOUND", - "SQLITE_OPEN_CREATE", - "SQLITE_OPEN_DELETEONCLOSE", - "SQLITE_OPEN_MAIN_DB", - "SQLITE_OPEN_READONLY" - ].forEach((k) => { - if (void 0 === (state.sq3Codes[k] = capi[k])) toss("Maintenance required: not found:", k); - }); - state.opfsFlags = Object.assign(Object.create(null), { - OPFS_UNLOCK_ASAP: 1, - OPFS_UNLINK_BEFORE_OPEN: 2, - defaultUnlockAsap: false - }); - /** - Runs the given operation (by name) in the async worker - counterpart, waits for its response, and returns the result - which the async worker writes to SAB[state.opIds.rc]. The - 2nd and subsequent arguments must be the arguments for the - async op. - */ - const opRun = (op, ...args) => { - const opNdx = state.opIds[op] || toss("Invalid op ID:", op); - state.s11n.serialize(...args); - Atomics.store(state.sabOPView, state.opIds.rc, -1); - Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); - Atomics.notify(state.sabOPView, state.opIds.whichOp); - const t = performance.now(); - while ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.rc, -1)); - const rc = Atomics.load(state.sabOPView, state.opIds.rc); - metrics[op].wait += performance.now() - t; - if (rc && state.asyncS11nExceptions) { - const err = state.s11n.deserialize(); - if (err) error(op + "() async error:", ...err); - } - return rc; - }; - /** - Not part of the public API. Only for test/development use. - */ - opfsUtil.debug = { - asyncShutdown: () => { - warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); - opRun("opfs-async-shutdown"); - }, - asyncRestart: () => { - warn("Attempting to restart OPFS VFS async listener. Might work, might not."); - W.postMessage({ type: "opfs-async-restart" }); - } - }; - const initS11n = () => { - /** - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ACHTUNG: this code is 100% duplicated in the other half of - this proxy! The documentation is maintained in the - "synchronous half". - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - This proxy de/serializes cross-thread function arguments and - output-pointer values via the state.sabIO SharedArrayBuffer, - using the region defined by (state.sabS11nOffset, - state.sabS11nOffset + state.sabS11nSize]. Only one dataset is - recorded at a time. - - This is not a general-purpose format. It only supports the - range of operations, and data sizes, needed by the - sqlite3_vfs and sqlite3_io_methods operations. Serialized - data are transient and this serialization algorithm may - change at any time. - - The data format can be succinctly summarized as: - - Nt...Td...D - - Where: - - - N = number of entries (1 byte) - - - t = type ID of first argument (1 byte) - - - ...T = type IDs of the 2nd and subsequent arguments (1 byte - each). - - - d = raw bytes of first argument (per-type size). - - - ...D = raw bytes of the 2nd and subsequent arguments (per-type - size). - - All types except strings have fixed sizes. Strings are stored - using their TextEncoder/TextDecoder representations. It would - arguably make more sense to store them as Int16Arrays of - their JS character values, but how best/fastest to get that - in and out of string form is an open point. Initial - experimentation with that approach did not gain us any speed. - - Historical note: this impl was initially about 1% this size by - using using JSON.stringify/parse(), but using fit-to-purpose - serialization saves considerable runtime. - */ - if (state.s11n) return state.s11n; - const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - state.s11n = Object.create(null); - const TypeIds = Object.create(null); - TypeIds.number = { - id: 1, - size: 8, - getter: "getFloat64", - setter: "setFloat64" - }; - TypeIds.bigint = { - id: 2, - size: 8, - getter: "getBigInt64", - setter: "setBigInt64" - }; - TypeIds.boolean = { - id: 3, - size: 4, - getter: "getInt32", - setter: "setInt32" - }; - TypeIds.string = { id: 4 }; - const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v); - const getTypeIdById = (tid) => { - switch (tid) { - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:", tid); - } - }; - /** - Returns an array of the deserialized state stored by the most - recent serialize() operation (from this thread or the - counterpart thread), or null if the serialization buffer is - empty. If passed a truthy argument, the serialization buffer - is cleared after deserialization. - */ - state.s11n.deserialize = function(clear = false) { - ++metrics.s11n.deserialize.count; - const t = performance.now(); - const argc = viewU8[0]; - const rc = argc ? [] : null; - if (argc) { - const typeIds = []; - let offset = 1, i, n, v; - for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset])); - for (i = 0; i < argc; ++i) { - const t = typeIds[i]; - if (t.getter) { - v = viewDV[t.getter](offset, state.littleEndian); - offset += t.size; - } else { - n = viewDV.getInt32(offset, state.littleEndian); - offset += 4; - v = textDecoder.decode(viewU8.slice(offset, offset + n)); - offset += n; - } - rc.push(v); - } - } - if (clear) viewU8[0] = 0; - metrics.s11n.deserialize.time += performance.now() - t; - return rc; - }; - /** - Serializes all arguments to the shared buffer for consumption - by the counterpart thread. - - This routine is only intended for serializing OPFS VFS - arguments and (in at least one special case) result values, - and the buffer is sized to be able to comfortably handle - those. - - If passed no arguments then it zeroes out the serialization - state. - */ - state.s11n.serialize = function(...args) { - const t = performance.now(); - ++metrics.s11n.serialize.count; - if (args.length) { - const typeIds = []; - let i = 0, offset = 1; - viewU8[0] = args.length & 255; - for (; i < args.length; ++i, ++offset) { - typeIds.push(getTypeId(args[i])); - viewU8[offset] = typeIds[i].id; - } - for (i = 0; i < args.length; ++i) { - const t = typeIds[i]; - if (t.setter) { - viewDV[t.setter](offset, args[i], state.littleEndian); - offset += t.size; - } else { - const s = textEncoder.encode(args[i]); - viewDV.setInt32(offset, s.byteLength, state.littleEndian); - offset += 4; - viewU8.set(s, offset); - offset += s.byteLength; - } - } - } else viewU8[0] = 0; - metrics.s11n.serialize.time += performance.now() - t; - }; - return state.s11n; - }; - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len = 16) { - if (!f._chars) { - f._chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for (; i < len; ++i) { - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(""); - }; - /** - Map of sqlite3_file pointers to objects constructed by xOpen(). - */ - const __openFiles = Object.create(null); - const opTimer = Object.create(null); - opTimer.op = void 0; - opTimer.start = void 0; - const mTimeStart = (op) => { - opTimer.start = performance.now(); - opTimer.op = op; - ++metrics[op].count; - }; - const mTimeEnd = () => metrics[opTimer.op].time += performance.now() - opTimer.start; - /** - Impls for the sqlite3_io_methods methods. Maintenance reminder: - members are in alphabetical order to simplify finding them. - */ - const ioSyncWrappers = { - xCheckReservedLock: function(pFile, pOut) { - /** - As of late 2022, only a single lock can be held on an OPFS - file. We have no way of checking whether any _other_ db - connection has a lock except by trying to obtain and (on - success) release a sync-handle for it, but doing so would - involve an inherent race condition. For the time being, - pending a better solution, we simply report whether the - given pFile is open. - - Update 2024-06-12: based on forum discussions, this - function now always sets pOut to 0 (false): - - https://sqlite.org/forum/forumpost/a2f573b00cda1372 - */ - wasm.poke(pOut, 0, "i32"); - return 0; - }, - xClose: function(pFile) { - mTimeStart("xClose"); - let rc = 0; - const f = __openFiles[pFile]; - if (f) { - delete __openFiles[pFile]; - rc = opRun("xClose", pFile); - if (f.sq3File) f.sq3File.dispose(); - } - mTimeEnd(); - return rc; - }, - xDeviceCharacteristics: function(pFile) { - return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; - }, - xFileControl: function(pFile, opId, pArg) { - return capi.SQLITE_NOTFOUND; - }, - xFileSize: function(pFile, pSz64) { - mTimeStart("xFileSize"); - let rc = opRun("xFileSize", pFile); - if (0 == rc) try { - const sz = state.s11n.deserialize()[0]; - wasm.poke(pSz64, sz, "i64"); - } catch (e) { - error("Unexpected error reading xFileSize() result:", e); - rc = state.sq3Codes.SQLITE_IOERR; - } - mTimeEnd(); - return rc; - }, - xLock: function(pFile, lockType) { - mTimeStart("xLock"); - const f = __openFiles[pFile]; - let rc = 0; - if (!f.lockType) { - rc = opRun("xLock", pFile, lockType); - if (0 === rc) f.lockType = lockType; - } else f.lockType = lockType; - mTimeEnd(); - return rc; - }, - xRead: function(pFile, pDest, n, offset64) { - mTimeStart("xRead"); - const f = __openFiles[pFile]; - let rc; - try { - rc = opRun("xRead", pFile, n, Number(offset64)); - if (0 === rc || capi.SQLITE_IOERR_SHORT_READ === rc) - /** - Results get written to the SharedArrayBuffer f.sabView. - Because the heap is _not_ a SharedArrayBuffer, we have - to copy the results. TypedArray.set() seems to be the - fastest way to copy this. */ - wasm.heap8u().set(f.sabView.subarray(0, n), Number(pDest)); - } catch (e) { - error("xRead(", arguments, ") failed:", e, f); - rc = capi.SQLITE_IOERR_READ; - } - mTimeEnd(); - return rc; - }, - xSync: function(pFile, flags) { - mTimeStart("xSync"); - ++metrics.xSync.count; - const rc = opRun("xSync", pFile, flags); - mTimeEnd(); - return rc; - }, - xTruncate: function(pFile, sz64) { - mTimeStart("xTruncate"); - const rc = opRun("xTruncate", pFile, Number(sz64)); - mTimeEnd(); - return rc; - }, - xUnlock: function(pFile, lockType) { - mTimeStart("xUnlock"); - const f = __openFiles[pFile]; - let rc = 0; - if (capi.SQLITE_LOCK_NONE === lockType && f.lockType) rc = opRun("xUnlock", pFile, lockType); - if (0 === rc) f.lockType = lockType; - mTimeEnd(); - return rc; - }, - xWrite: function(pFile, pSrc, n, offset64) { - mTimeStart("xWrite"); - const f = __openFiles[pFile]; - let rc; - try { - f.sabView.set(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n)); - rc = opRun("xWrite", pFile, n, Number(offset64)); - } catch (e) { - error("xWrite(", arguments, ") failed:", e, f); - rc = capi.SQLITE_IOERR_WRITE; - } - mTimeEnd(); - return rc; - } - }; - /** - Impls for the sqlite3_vfs methods. Maintenance reminder: members - are in alphabetical order to simplify finding them. - */ - const vfsSyncWrappers = { - xAccess: function(pVfs, zName, flags, pOut) { - mTimeStart("xAccess"); - const rc = opRun("xAccess", wasm.cstrToJs(zName)); - wasm.poke(pOut, rc ? 0 : 1, "i32"); - mTimeEnd(); - return 0; - }, - xCurrentTime: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); - return 0; - }, - xCurrentTimeInt64: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); - return 0; - }, - xDelete: function(pVfs, zName, doSyncDir) { - mTimeStart("xDelete"); - const rc = opRun("xDelete", wasm.cstrToJs(zName), doSyncDir, false); - mTimeEnd(); - return rc; - }, - xFullPathname: function(pVfs, zName, nOut, pOut) { - return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; - }, - xGetLastError: function(pVfs, nOut, pOut) { - warn("OPFS xGetLastError() has nothing sensible to return."); - return 0; - }, - xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { - mTimeStart("xOpen"); - let opfsFlags = 0; - if (0 === zName) zName = randomFilename(); - else if (wasm.isPtr(zName)) { - if (capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP; - if (capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; - zName = wasm.cstrToJs(zName); - } - const fh = Object.create(null); - fh.fid = pFile; - fh.filename = zName; - fh.sab = new SharedArrayBuffer(state.fileBufferSize); - fh.flags = flags; - fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) && !!(flags & capi.SQLITE_OPEN_READONLY); - const rc = opRun("xOpen", pFile, zName, flags, opfsFlags); - if (!rc) { - if (fh.readOnly) wasm.poke(pOutFlags, capi.SQLITE_OPEN_READONLY, "i32"); - __openFiles[pFile] = fh; - fh.sabView = state.sabFileBufView; - fh.sq3File = new sqlite3_file(pFile); - fh.sq3File.$pMethods = opfsIoMethods.pointer; - fh.lockType = capi.SQLITE_LOCK_NONE; - } - mTimeEnd(); - return rc; - } - }; - if (dVfs) { - opfsVfs.$xRandomness = dVfs.$xRandomness; - opfsVfs.$xSleep = dVfs.$xSleep; - } - if (!opfsVfs.$xRandomness) vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut) { - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; - return i; - }; - if (!opfsVfs.$xSleep) vfsSyncWrappers.xSleep = function(pVfs, ms) { - Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); - return 0; - }; - /** - Expects an OPFS file path. It gets resolved, such that ".." - components are properly expanded, and returned. If the 2nd arg - is true, the result is returned as an array of path elements, - else an absolute path string is returned. - */ - opfsUtil.getResolvedPath = function(filename, splitIt) { - const p = new URL(filename, "file://irrelevant").pathname; - return splitIt ? p.split("/").filter((v) => !!v) : p; - }; - /** - Takes the absolute path to a filesystem element. Returns an - array of [handleOfContainingDir, filename]. If the 2nd argument - is truthy then each directory element leading to the file is - created along the way. Throws if any creation or resolution - fails. - */ - opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false) { - const path = opfsUtil.getResolvedPath(absFilename, true); - const filename = path.pop(); - let dh = opfsUtil.rootDirectory; - for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); - return [dh, filename]; - }; - /** - Creates the given directory name, recursively, in - the OPFS filesystem. Returns true if it succeeds or the - directory already exists, else false. - */ - opfsUtil.mkdir = async function(absDirName) { - try { - await opfsUtil.getDirForFilename(absDirName + "/filepart", true); - return true; - } catch (e) { - return false; - } - }; - /** - Checks whether the given OPFS filesystem entry exists, - returning true if it does, false if it doesn't or if an - exception is intercepted while trying to make the - determination. - */ - opfsUtil.entryExists = async function(fsEntryName) { - try { - const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); - await dh.getFileHandle(fn); - return true; - } catch (e) { - return false; - } - }; - /** - Generates a random ASCII string, intended for use as a - temporary file name. Its argument is the length of the string, - defaulting to 16. - */ - opfsUtil.randomFilename = randomFilename; - /** - Returns a promise which resolves to an object which represents - all files and directories in the OPFS tree. The top-most object - has two properties: `dirs` is an array of directory entries - (described below) and `files` is a list of file names for all - files in that directory. - - Traversal starts at sqlite3.opfs.rootDirectory. - - Each `dirs` entry is an object in this form: - - ``` - { name: directoryName, - dirs: [...subdirs], - files: [...file names] - } - ``` - - The `files` and `subdirs` entries are always set but may be - empty arrays. - - The returned object has the same structure but its `name` is - an empty string. All returned objects are created with - Object.create(null), so have no prototype. - - Design note: the entries do not contain more information, - e.g. file sizes, because getting such info is not only - expensive but is subject to locking-related errors. - */ - opfsUtil.treeList = async function() { - const doDir = async function callee(dirHandle, tgt) { - tgt.name = dirHandle.name; - tgt.dirs = []; - tgt.files = []; - for await (const handle of dirHandle.values()) if ("directory" === handle.kind) { - const subDir = Object.create(null); - tgt.dirs.push(subDir); - await callee(handle, subDir); - } else tgt.files.push(handle.name); - }; - const root = Object.create(null); - await doDir(opfsUtil.rootDirectory, root); - return root; - }; - /** - Irrevocably deletes _all_ files in the current origin's OPFS. - Obviously, this must be used with great caution. It may throw - an exception if removal of anything fails (e.g. a file is - locked), but the precise conditions under which the underlying - APIs will throw are not documented (so we cannot tell you what - they are). - */ - opfsUtil.rmfr = async function() { - const dir = opfsUtil.rootDirectory, opt = { recurse: true }; - for await (const handle of dir.values()) dir.removeEntry(handle.name, opt); - }; - /** - Deletes the given OPFS filesystem entry. As this environment - has no notion of "current directory", the given name must be an - absolute path. If the 2nd argument is truthy, deletion is - recursive (use with caution!). - - The returned Promise resolves to true if the deletion was - successful, else false (but...). The OPFS API reports the - reason for the failure only in human-readable form, not - exceptions which can be type-checked to determine the - failure. Because of that... - - If the final argument is truthy then this function will - propagate any exception on error, rather than returning false. - */ - opfsUtil.unlink = async function(fsEntryName, recursive = false, throwOnError = false) { - try { - const [hDir, filenamePart] = await opfsUtil.getDirForFilename(fsEntryName, false); - await hDir.removeEntry(filenamePart, { recursive }); - return true; - } catch (e) { - if (throwOnError) throw new Error("unlink(", arguments[0], ") failed: " + e.message, { cause: e }); - return false; - } - }; - /** - Traverses the OPFS filesystem, calling a callback for each - entry. The argument may be either a callback function or an - options object with any of the following properties: - - - `callback`: function which gets called for each filesystem - entry. It gets passed 3 arguments: 1) the - FileSystemFileHandle or FileSystemDirectoryHandle of each - entry (noting that both are instanceof FileSystemHandle). 2) - the FileSystemDirectoryHandle of the parent directory. 3) the - current depth level, with 0 being at the top of the tree - relative to the starting directory. If the callback returns a - literal false, as opposed to any other falsy value, traversal - stops without an error. Any exceptions it throws are - propagated. Results are undefined if the callback manipulate - the filesystem (e.g. removing or adding entries) because the - how OPFS iterators behave in the face of such changes is - undocumented. - - - `recursive` [bool=true]: specifies whether to recurse into - subdirectories or not. Whether recursion is depth-first or - breadth-first is unspecified! - - - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] - specifies the starting directory. - - If this function is passed a function, it is assumed to be the - callback. - - Returns a promise because it has to (by virtue of being async) - but that promise has no specific meaning: the traversal it - performs is synchronous. The promise must be used to catch any - exceptions propagated by the callback, however. - */ - opfsUtil.traverse = async function(opt) { - const defaultOpt = { - recursive: true, - directory: opfsUtil.rootDirectory - }; - if ("function" === typeof opt) opt = { callback: opt }; - opt = Object.assign(defaultOpt, opt || {}); - (async function callee(dirHandle, depth) { - for await (const handle of dirHandle.values()) if (false === opt.callback(handle, dirHandle, depth)) return false; - else if (opt.recursive && "directory" === handle.kind) { - if (false === await callee(handle, depth + 1)) break; - } - })(opt.directory, 0); - }; - /** - impl of importDb() when it's given a function as its second - argument. - */ - const importDbChunked = async function(filename, callback) { - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - let sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(), nWrote = 0, chunk, checkedHeader = false; - try { - sah.truncate(0); - while (void 0 !== (chunk = await callback())) { - if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); - if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { - util.affirmDbHeader(chunk); - checkedHeader = true; - } - sah.write(chunk, { at: nWrote }); - nWrote += chunk.byteLength; - } - if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); - if (!checkedHeader) { - const header = new Uint8Array(20); - sah.read(header, { at: 0 }); - util.affirmDbHeader(header); - } - sah.write(new Uint8Array([1, 1]), { at: 18 }); - return nWrote; - } catch (e) { - await sah.close(); - sah = void 0; - await hDir.removeEntry(fnamePart).catch(() => {}); - throw e; - } finally { - if (sah) await sah.close(); - } - }; - /** - Asynchronously imports the given bytes (a byte array or - ArrayBuffer) into the given database file. - - Results are undefined if the given db name refers to an opened - db. - - If passed a function for its second argument, its behaviour - changes: imports its data in chunks fed to it by the given - callback function. It calls the callback (which may be async) - repeatedly, expecting either a Uint8Array or ArrayBuffer (to - denote new input) or undefined (to denote EOF). For so long as - the callback continues to return non-undefined, it will append - incoming data to the given VFS-hosted database file. When - called this way, the resolved value of the returned Promise is - the number of bytes written to the target file. - - It very specifically requires the input to be an SQLite3 - database and throws if that's not the case. It does so in - order to prevent this function from taking on a larger scope - than it is specifically intended to. i.e. we do not want it to - become a convenience for importing arbitrary files into OPFS. - - This routine rewrites the database header bytes in the output - file (not the input array) to force disabling of WAL mode. - - On error this throws and the state of the input file is - undefined (it depends on where the exception was triggered). - - On success, resolves to the number of bytes written. - */ - opfsUtil.importDb = async function(filename, bytes) { - if (bytes instanceof Function) return importDbChunked(filename, bytes); - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - util.affirmIsDb(bytes); - const n = bytes.byteLength; - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - let sah, nWrote = 0; - try { - sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(); - sah.truncate(0); - nWrote = sah.write(bytes, { at: 0 }); - if (nWrote != n) toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); - sah.write(new Uint8Array([1, 1]), { at: 18 }); - return nWrote; - } catch (e) { - if (sah) { - await sah.close(); - sah = void 0; - } - await hDir.removeEntry(fnamePart).catch(() => {}); - throw e; - } finally { - if (sah) await sah.close(); - } - }; - if (sqlite3.oo1) { - const OpfsDb = function(...args) { - const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); - opt.vfs = opfsVfs.$zName; - sqlite3.oo1.DB.dbCtorHelper.call(this, opt); - }; - OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); - sqlite3.oo1.OpfsDb = OpfsDb; - OpfsDb.importDb = opfsUtil.importDb; - sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback(opfsVfs.pointer, function(oo1Db, sqlite3) { - sqlite3.capi.sqlite3_busy_timeout(oo1Db, 1e4); - }); - } - const sanityCheck = function() { - const scope = wasm.scopedAllocPush(); - const sq3File = new sqlite3_file(); - try { - const fid = sq3File.pointer; - const openFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE | capi.SQLITE_OPEN_MAIN_DB; - const pOut = wasm.scopedAlloc(8); - const dbFile = "/sanity/check/file" + randomFilename(8); - const zDbFile = wasm.scopedAllocCString(dbFile); - let rc; - state.s11n.serialize("This is ä string."); - rc = state.s11n.deserialize(); - log("deserialize() says:", rc); - if ("This is ä string." !== rc[0]) toss("String d13n error."); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut, "i32"); - log("xAccess(", dbFile, ") exists ?=", rc); - rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, fid, openFlags, pOut); - log("open rc =", rc, "state.sabOPView[xOpen] =", state.sabOPView[state.opIds.xOpen]); - if (0 !== rc) { - error("open failed with code", rc); - return; - } - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut, "i32"); - if (!rc) toss("xAccess() failed to detect file."); - rc = ioSyncWrappers.xSync(sq3File.pointer, 0); - if (rc) toss("sync failed w/ rc", rc); - rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); - if (rc) toss("truncate failed w/ rc", rc); - wasm.poke(pOut, 0, "i64"); - rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); - if (rc) toss("xFileSize failed w/ rc", rc); - log("xFileSize says:", wasm.peek(pOut, "i64")); - rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); - if (rc) toss("xWrite() failed!"); - const readBuf = wasm.scopedAlloc(16); - rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); - wasm.poke(readBuf + 6, 0); - let jRead = wasm.cstrToJs(readBuf); - log("xRead() got:", jRead); - if ("sanity" !== jRead) toss("Unexpected xRead() value."); - if (vfsSyncWrappers.xSleep) { - log("xSleep()ing before close()ing..."); - vfsSyncWrappers.xSleep(opfsVfs.pointer, 2e3); - log("waking up from xSleep()"); - } - rc = ioSyncWrappers.xClose(fid); - log("xClose rc =", rc, "sabOPView =", state.sabOPView); - log("Deleting file:", dbFile); - vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 4660); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut, "i32"); - if (rc) toss("Expecting 0 from xAccess(", dbFile, ") after xDelete()."); - warn("End of OPFS sanity checks."); - } finally { - sq3File.dispose(); - wasm.scopedAllocPop(scope); - } - }; - W.onmessage = function({ data }) { - switch (data.type) { - case "opfs-unavailable": - promiseReject(new Error(data.payload.join(" "))); - break; - case "opfs-async-loaded": - W.postMessage({ - type: "opfs-async-init", - args: state - }); - break; - case "opfs-async-inited": - if (true === promiseWasRejected) break; - try { - sqlite3.vfs.installVfs({ - io: { - struct: opfsIoMethods, - methods: ioSyncWrappers - }, - vfs: { - struct: opfsVfs, - methods: vfsSyncWrappers - } - }); - state.sabOPView = new Int32Array(state.sabOP); - state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); - state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - initS11n(); - if (options.sanityChecks) { - warn("Running sanity checks because of opfs-sanity-check URL arg..."); - sanityCheck(); - } - if (thisThreadHasOPFS()) navigator.storage.getDirectory().then((d) => { - W.onerror = W._originalOnError; - delete W._originalOnError; - sqlite3.opfs = opfsUtil; - opfsUtil.rootDirectory = d; - log("End of OPFS sqlite3_vfs setup.", opfsVfs); - promiseResolve(); - }).catch(promiseReject); - else promiseResolve(); - } catch (e) { - error(e); - promiseReject(e); - } - break; - default: { - const errMsg = "Unexpected message from the OPFS async worker: " + JSON.stringify(data); - error(errMsg); - promiseReject(new Error(errMsg)); - break; - } - } - }; - }); - }; - installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js"; - globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3) => { - try { - let proxyJs = installOpfsVfs.defaultProxyUri; - if (sqlite3?.scriptInfo?.sqlite3Dir) installOpfsVfs.defaultProxyUri = sqlite3.scriptInfo.sqlite3Dir + proxyJs; - return installOpfsVfs().catch((e) => { - sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:", e.message); - }); - } catch (e) { - sqlite3.config.error("installOpfsVfs() exception:", e); - return Promise.reject(e); - } - }); - }); - globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) { - "use strict"; - const toss = sqlite3.util.toss; - const toss3 = sqlite3.util.toss3; - const initPromises = Object.create(null); - const capi = sqlite3.capi; - const util = sqlite3.util; - const wasm = sqlite3.wasm; - const SECTOR_SIZE = 4096; - const HEADER_MAX_PATH_SIZE = 512; - const HEADER_FLAGS_SIZE = 4; - const HEADER_DIGEST_SIZE = 8; - const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE; - const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE; - const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE; - const HEADER_OFFSET_DATA = SECTOR_SIZE; - const PERSISTENT_FILE_TYPES = capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_MAIN_JOURNAL | capi.SQLITE_OPEN_SUPER_JOURNAL | capi.SQLITE_OPEN_WAL; - const FLAG_COMPUTE_DIGEST_V2 = capi.SQLITE_OPEN_MEMORY; - /** Subdirectory of the VFS's space where "opaque" (randomly-named) - files are stored. Changing this effectively invalidates the data - stored under older names (orphaning it), so don't do that. */ - const OPAQUE_DIR_NAME = ".opaque"; - /** - Returns short a string of random alphanumeric characters - suitable for use as a random filename. - */ - const getRandomName = () => Math.random().toString(36).slice(2); - const textDecoder = new TextDecoder(); - const textEncoder = new TextEncoder(); - const optionDefaults = Object.assign(Object.create(null), { - name: "opfs-sahpool", - directory: void 0, - initialCapacity: 6, - clearOnInit: false, - verbosity: 2, - forceReinitIfPreviouslyFailed: false - }); - /** Logging routines, from most to least serious. */ - const loggers = [ - sqlite3.config.error, - sqlite3.config.warn, - sqlite3.config.log - ]; - sqlite3.config.log; - const warn = sqlite3.config.warn; - sqlite3.config.error; - const __mapVfsToPool = /* @__PURE__ */ new Map(); - const getPoolForVfs = (pVfs) => __mapVfsToPool.get(pVfs); - const setPoolForVfs = (pVfs, pool) => { - if (pool) __mapVfsToPool.set(pVfs, pool); - else __mapVfsToPool.delete(pVfs); - }; - const __mapSqlite3File = /* @__PURE__ */ new Map(); - const getPoolForPFile = (pFile) => __mapSqlite3File.get(pFile); - const setPoolForPFile = (pFile, pool) => { - if (pool) __mapSqlite3File.set(pFile, pool); - else __mapSqlite3File.delete(pFile); - }; - /** - Impls for the sqlite3_io_methods methods. Maintenance reminder: - members are in alphabetical order to simplify finding them. - */ - const ioMethods = { - xCheckReservedLock: function(pFile, pOut) { - const pool = getPoolForPFile(pFile); - pool.log("xCheckReservedLock"); - pool.storeErr(); - wasm.poke32(pOut, 1); - return 0; - }, - xClose: function(pFile) { - const pool = getPoolForPFile(pFile); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - if (file) try { - pool.log(`xClose ${file.path}`); - pool.mapS3FileToOFile(pFile, false); - file.sah.flush(); - if (file.flags & capi.SQLITE_OPEN_DELETEONCLOSE) pool.deletePath(file.path); - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - return 0; - }, - xDeviceCharacteristics: function(pFile) { - return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; - }, - xFileControl: function(pFile, opId, pArg) { - return capi.SQLITE_NOTFOUND; - }, - xFileSize: function(pFile, pSz64) { - const pool = getPoolForPFile(pFile); - pool.log(`xFileSize`); - const size = pool.getOFileForS3File(pFile).sah.getSize() - HEADER_OFFSET_DATA; - wasm.poke64(pSz64, BigInt(size)); - return 0; - }, - xLock: function(pFile, lockType) { - const pool = getPoolForPFile(pFile); - pool.log(`xLock ${lockType}`); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - file.lockType = lockType; - return 0; - }, - xRead: function(pFile, pDest, n, offset64) { - const pool = getPoolForPFile(pFile); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - pool.log(`xRead ${file.path} ${n} @ ${offset64}`); - try { - const nRead = file.sah.read(wasm.heap8u().subarray(Number(pDest), Number(pDest) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }); - if (nRead < n) { - wasm.heap8u().fill(0, Number(pDest) + nRead, Number(pDest) + n); - return capi.SQLITE_IOERR_SHORT_READ; - } - return 0; - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - }, - xSectorSize: function(pFile) { - return SECTOR_SIZE; - }, - xSync: function(pFile, flags) { - const pool = getPoolForPFile(pFile); - pool.log(`xSync ${flags}`); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - try { - file.sah.flush(); - return 0; - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - }, - xTruncate: function(pFile, sz64) { - const pool = getPoolForPFile(pFile); - pool.log(`xTruncate ${sz64}`); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - try { - file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64)); - return 0; - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - }, - xUnlock: function(pFile, lockType) { - const pool = getPoolForPFile(pFile); - pool.log("xUnlock"); - const file = pool.getOFileForS3File(pFile); - file.lockType = lockType; - return 0; - }, - xWrite: function(pFile, pSrc, n, offset64) { - const pool = getPoolForPFile(pFile); - pool.storeErr(); - const file = pool.getOFileForS3File(pFile); - pool.log(`xWrite ${file.path} ${n} ${offset64}`); - try { - return n === file.sah.write(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }) ? 0 : toss("Unknown write() failure."); - } catch (e) { - return pool.storeErr(e, capi.SQLITE_IOERR); - } - } - }; - const opfsIoMethods = new capi.sqlite3_io_methods(); - opfsIoMethods.$iVersion = 1; - sqlite3.vfs.installVfs({ io: { - struct: opfsIoMethods, - methods: ioMethods - } }); - /** - Impls for the sqlite3_vfs methods. Maintenance reminder: members - are in alphabetical order to simplify finding them. - */ - const vfsMethods = { - xAccess: function(pVfs, zName, flags, pOut) { - const pool = getPoolForVfs(pVfs); - pool.storeErr(); - try { - const name = pool.getPath(zName); - wasm.poke32(pOut, pool.hasFilename(name) ? 1 : 0); - } catch (e) { - wasm.poke32(pOut, 0); - } - return 0; - }, - xCurrentTime: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double"); - return 0; - }, - xCurrentTimeInt64: function(pVfs, pOut) { - wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64"); - return 0; - }, - xDelete: function(pVfs, zName, doSyncDir) { - const pool = getPoolForVfs(pVfs); - pool.log(`xDelete ${wasm.cstrToJs(zName)}`); - pool.storeErr(); - try { - pool.deletePath(pool.getPath(zName)); - return 0; - } catch (e) { - pool.storeErr(e); - return capi.SQLITE_IOERR_DELETE; - } - }, - xFullPathname: function(pVfs, zName, nOut, pOut) { - return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN; - }, - xGetLastError: function(pVfs, nOut, pOut) { - const pool = getPoolForVfs(pVfs); - const e = pool.popErr(); - pool.log(`xGetLastError ${nOut} e =`, e); - if (e) { - const scope = wasm.scopedAllocPush(); - try { - const [cMsg, n] = wasm.scopedAllocCString(e.message, true); - wasm.cstrncpy(pOut, cMsg, nOut); - if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0); - } catch (e) { - return capi.SQLITE_NOMEM; - } finally { - wasm.scopedAllocPop(scope); - } - } - return e ? e.sqlite3Rc || capi.SQLITE_IOERR : 0; - }, - xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) { - const pool = getPoolForVfs(pVfs); - try { - flags &= ~FLAG_COMPUTE_DIGEST_V2; - pool.log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`); - const path = zName && wasm.peek8(zName) ? pool.getPath(zName) : getRandomName(); - let sah = pool.getSAHForPath(path); - if (!sah && flags & capi.SQLITE_OPEN_CREATE) if (pool.getFileCount() < pool.getCapacity()) { - sah = pool.nextAvailableSAH(); - pool.setAssociatedPath(sah, path, flags); - } else toss("SAH pool is full. Cannot create file", path); - if (!sah) toss("file not found:", path); - const file = { - path, - flags, - sah - }; - pool.mapS3FileToOFile(pFile, file); - file.lockType = capi.SQLITE_LOCK_NONE; - const sq3File = new capi.sqlite3_file(pFile); - sq3File.$pMethods = opfsIoMethods.pointer; - sq3File.dispose(); - wasm.poke32(pOutFlags, flags); - return 0; - } catch (e) { - pool.storeErr(e); - return capi.SQLITE_CANTOPEN; - } - } - }; - /** - Creates, initializes, and returns an sqlite3_vfs instance for an - OpfsSAHPool. The argument is the VFS's name (JS string). - - Throws if the VFS name is already registered or if something - goes terribly wrong via sqlite3.vfs.installVfs(). - - Maintenance reminder: the only detail about the returned object - which is specific to any given OpfsSAHPool instance is the $zName - member. All other state is identical. - */ - const createOpfsVfs = function(vfsName) { - if (sqlite3.capi.sqlite3_vfs_find(vfsName)) toss3("VFS name is already registered:", vfsName); - const opfsVfs = new capi.sqlite3_vfs(); - const pDVfs = capi.sqlite3_vfs_find(null); - const dVfs = pDVfs ? new capi.sqlite3_vfs(pDVfs) : null; - opfsVfs.$iVersion = 2; - opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE; - opfsVfs.addOnDispose(opfsVfs.$zName = wasm.allocCString(vfsName), () => setPoolForVfs(opfsVfs.pointer, 0)); - if (dVfs) { - opfsVfs.$xRandomness = dVfs.$xRandomness; - opfsVfs.$xSleep = dVfs.$xSleep; - dVfs.dispose(); - } - if (!opfsVfs.$xRandomness && !vfsMethods.xRandomness) vfsMethods.xRandomness = function(pVfs, nOut, pOut) { - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255; - return i; - }; - if (!opfsVfs.$xSleep && !vfsMethods.xSleep) vfsMethods.xSleep = (pVfs, ms) => 0; - sqlite3.vfs.installVfs({ vfs: { - struct: opfsVfs, - methods: vfsMethods - } }); - return opfsVfs; - }; - /** - Class for managing OPFS-related state for the - OPFS SharedAccessHandle Pool sqlite3_vfs. - */ - class OpfsSAHPool { - vfsDir; - #dhVfsRoot; - #dhOpaque; - #dhVfsParent; - #mapSAHToName = /* @__PURE__ */ new Map(); - #mapFilenameToSAH = /* @__PURE__ */ new Map(); - #availableSAH = /* @__PURE__ */ new Set(); - #mapS3FileToOFile_ = /* @__PURE__ */ new Map(); - /** Buffer used by [sg]etAssociatedPath(). */ - #apBody = new Uint8Array(HEADER_CORPUS_SIZE); - #dvBody; - #cVfs; - #verbosity; - constructor(options = Object.create(null)) { - this.#verbosity = options.verbosity ?? optionDefaults.verbosity; - this.vfsName = options.name || optionDefaults.name; - this.#cVfs = createOpfsVfs(this.vfsName); - setPoolForVfs(this.#cVfs.pointer, this); - this.vfsDir = options.directory || "." + this.vfsName; - this.#dvBody = new DataView(this.#apBody.buffer, this.#apBody.byteOffset); - this.isReady = this.reset(!!(options.clearOnInit ?? optionDefaults.clearOnInit)).then(() => { - if (this.$error) throw this.$error; - return this.getCapacity() ? Promise.resolve(void 0) : this.addCapacity(options.initialCapacity || optionDefaults.initialCapacity); - }); - } - #logImpl(level, ...args) { - if (this.#verbosity > level) loggers[level](this.vfsName + ":", ...args); - } - log(...args) { - this.#logImpl(2, ...args); - } - warn(...args) { - this.#logImpl(1, ...args); - } - error(...args) { - this.#logImpl(0, ...args); - } - getVfs() { - return this.#cVfs; - } - getCapacity() { - return this.#mapSAHToName.size; - } - getFileCount() { - return this.#mapFilenameToSAH.size; - } - getFileNames() { - const rc = []; - for (const n of this.#mapFilenameToSAH.keys()) rc.push(n); - return rc; - } - /** - Adds n files to the pool's capacity. This change is - persistent across settings. Returns a Promise which resolves - to the new capacity. - */ - async addCapacity(n) { - for (let i = 0; i < n; ++i) { - const name = getRandomName(); - const ah = await (await this.#dhOpaque.getFileHandle(name, { create: true })).createSyncAccessHandle(); - this.#mapSAHToName.set(ah, name); - this.setAssociatedPath(ah, "", 0); - } - return this.getCapacity(); - } - /** - Reduce capacity by n, but can only reduce up to the limit - of currently-available SAHs. Returns a Promise which resolves - to the number of slots really removed. - */ - async reduceCapacity(n) { - let nRm = 0; - for (const ah of Array.from(this.#availableSAH)) { - if (nRm === n || this.getFileCount() === this.getCapacity()) break; - const name = this.#mapSAHToName.get(ah); - ah.close(); - await this.#dhOpaque.removeEntry(name); - this.#mapSAHToName.delete(ah); - this.#availableSAH.delete(ah); - ++nRm; - } - return nRm; - } - /** - Releases all currently-opened SAHs. The only legal operation - after this is acquireAccessHandles() or (if this is called from - pauseVfs()) either of isPaused() or unpauseVfs(). - */ - releaseAccessHandles() { - for (const ah of this.#mapSAHToName.keys()) ah.close(); - this.#mapSAHToName.clear(); - this.#mapFilenameToSAH.clear(); - this.#availableSAH.clear(); - } - /** - Opens all files under this.vfsDir/this.#dhOpaque and acquires a - SAH for each. Returns a Promise which resolves to no value but - completes once all SAHs are acquired. If acquiring an SAH - throws, this.$error will contain the corresponding Error - object. - - If it throws, it releases any SAHs which it may have - acquired before the exception was thrown, leaving the VFS in a - well-defined but unusable state. - - If clearFiles is true, the client-stored state of each file is - cleared when its handle is acquired, including its name, flags, - and any data stored after the metadata block. - */ - async acquireAccessHandles(clearFiles = false) { - const files = []; - for await (const [name, h] of this.#dhOpaque) if ("file" === h.kind) files.push([name, h]); - return Promise.all(files.map(async ([name, h]) => { - try { - const ah = await h.createSyncAccessHandle(); - this.#mapSAHToName.set(ah, name); - if (clearFiles) { - ah.truncate(HEADER_OFFSET_DATA); - this.setAssociatedPath(ah, "", 0); - } else { - const path = this.getAssociatedPath(ah); - if (path) this.#mapFilenameToSAH.set(path, ah); - else this.#availableSAH.add(ah); - } - } catch (e) { - this.storeErr(e); - this.releaseAccessHandles(); - throw e; - } - })); - } - /** - Given an SAH, returns the client-specified name of - that file by extracting it from the SAH's header. - - On error, it disassociates SAH from the pool and - returns an empty string. - */ - getAssociatedPath(sah) { - sah.read(this.#apBody, { at: 0 }); - const flags = this.#dvBody.getUint32(HEADER_OFFSET_FLAGS); - if (this.#apBody[0] && (flags & capi.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) { - warn(`Removing file with unexpected flags ${flags.toString(16)}`, this.#apBody); - this.setAssociatedPath(sah, "", 0); - return ""; - } - const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4); - sah.read(fileDigest, { at: HEADER_OFFSET_DIGEST }); - const compDigest = this.computeDigest(this.#apBody, flags); - if (fileDigest.every((v, i) => v === compDigest[i])) { - const pathBytes = this.#apBody.findIndex((v) => 0 === v); - if (0 === pathBytes) sah.truncate(HEADER_OFFSET_DATA); - return pathBytes ? textDecoder.decode(this.#apBody.subarray(0, pathBytes)) : ""; - } else { - warn("Disassociating file with bad digest."); - this.setAssociatedPath(sah, "", 0); - return ""; - } - } - /** - Stores the given client-defined path and SQLITE_OPEN_xyz flags - into the given SAH. If path is an empty string then the file is - disassociated from the pool but its previous name is preserved - in the metadata. - */ - setAssociatedPath(sah, path, flags) { - const enc = textEncoder.encodeInto(path, this.#apBody); - if (HEADER_MAX_PATH_SIZE <= enc.written + 1) toss("Path too long:", path); - if (path && flags) flags |= FLAG_COMPUTE_DIGEST_V2; - this.#apBody.fill(0, enc.written, HEADER_MAX_PATH_SIZE); - this.#dvBody.setUint32(HEADER_OFFSET_FLAGS, flags); - const digest = this.computeDigest(this.#apBody, flags); - sah.write(this.#apBody, { at: 0 }); - sah.write(digest, { at: HEADER_OFFSET_DIGEST }); - sah.flush(); - if (path) { - this.#mapFilenameToSAH.set(path, sah); - this.#availableSAH.delete(sah); - } else { - sah.truncate(HEADER_OFFSET_DATA); - this.#availableSAH.add(sah); - } - } - /** - Computes a digest for the given byte array and returns it as a - two-element Uint32Array. This digest gets stored in the - metadata for each file as a validation check. Changing this - algorithm invalidates all existing databases for this VFS, so - don't do that. - - See the docs for FLAG_COMPUTE_DIGEST_V2 for more details. - */ - computeDigest(byteArray, fileFlags) { - if (fileFlags & FLAG_COMPUTE_DIGEST_V2) { - let h1 = 3735928559; - let h2 = 1103547991; - for (const v of byteArray) { - h1 = Math.imul(h1 ^ v, 2654435761); - h2 = Math.imul(h2 ^ v, 104729); - } - return new Uint32Array([h1 >>> 0, h2 >>> 0]); - } else return new Uint32Array([0, 0]); - } - /** - Re-initializes the state of the SAH pool, releasing and - re-acquiring all handles. - - See acquireAccessHandles() for the specifics of the clearFiles - argument. - */ - async reset(clearFiles) { - await this.isReady; - let h = await navigator.storage.getDirectory(), prev; - for (const d of this.vfsDir.split("/")) if (d) { - prev = h; - h = await h.getDirectoryHandle(d, { create: true }); - } - this.#dhVfsRoot = h; - this.#dhVfsParent = prev; - this.#dhOpaque = await this.#dhVfsRoot.getDirectoryHandle(OPAQUE_DIR_NAME, { create: true }); - this.releaseAccessHandles(); - return this.acquireAccessHandles(clearFiles); - } - /** - Returns the pathname part of the given argument, - which may be any of: - - - a URL object - - A JS string representing a file name - - Wasm C-string representing a file name - - All "../" parts and duplicate slashes are resolve/removed from - the returned result. - */ - getPath(arg) { - if (wasm.isPtr(arg)) arg = wasm.cstrToJs(arg); - return (arg instanceof URL ? arg : new URL(arg, "file://localhost/")).pathname; - } - /** - Removes the association of the given client-specified file - name (JS string) from the pool. Returns true if a mapping - is found, else false. - */ - deletePath(path) { - const sah = this.#mapFilenameToSAH.get(path); - if (sah) { - this.#mapFilenameToSAH.delete(path); - this.setAssociatedPath(sah, "", 0); - } - return !!sah; - } - /** - Sets e (an Error object) as this object's current error. Pass a - falsy (or no) value to clear it. If code is truthy it is - assumed to be an SQLITE_xxx result code, defaulting to - SQLITE_IOERR if code is falsy. - - Returns the 2nd argument. - */ - storeErr(e, code) { - if (e) { - e.sqlite3Rc = code || capi.SQLITE_IOERR; - this.error(e); - } - this.$error = e; - return code; - } - /** - Pops this object's Error object and returns - it (a falsy value if no error is set). - */ - popErr() { - const rc = this.$error; - this.$error = void 0; - return rc; - } - /** - Returns the next available SAH without removing - it from the set. - */ - nextAvailableSAH() { - const [rc] = this.#availableSAH.keys(); - return rc; - } - /** - Given an (sqlite3_file*), returns the mapped - xOpen file object. - */ - getOFileForS3File(pFile) { - return this.#mapS3FileToOFile_.get(pFile); - } - /** - Maps or unmaps (if file is falsy) the given (sqlite3_file*) - to an xOpen file object and to this pool object. - */ - mapS3FileToOFile(pFile, file) { - if (file) { - this.#mapS3FileToOFile_.set(pFile, file); - setPoolForPFile(pFile, this); - } else { - this.#mapS3FileToOFile_.delete(pFile); - setPoolForPFile(pFile, false); - } - } - /** - Returns true if the given client-defined file name is in this - object's name-to-SAH map. - */ - hasFilename(name) { - return this.#mapFilenameToSAH.has(name); - } - /** - Returns the SAH associated with the given - client-defined file name. - */ - getSAHForPath(path) { - return this.#mapFilenameToSAH.get(path); - } - /** - Removes this object's sqlite3_vfs registration and shuts down - this object, releasing all handles, mappings, and whatnot, - including deleting its data directory. There is currently no - way to "revive" the object and reaquire its - resources. Similarly, there is no recovery strategy if removal - of any given SAH fails, so such errors are ignored by this - function. - - This function is intended primarily for testing. - - Resolves to true if it did its job, false if the - VFS has already been shut down. - - @see pauseVfs() - @see unpauseVfs() - */ - async removeVfs() { - if (!this.#cVfs.pointer || !this.#dhOpaque) return false; - capi.sqlite3_vfs_unregister(this.#cVfs.pointer); - this.#cVfs.dispose(); - delete initPromises[this.vfsName]; - try { - this.releaseAccessHandles(); - await this.#dhVfsRoot.removeEntry(OPAQUE_DIR_NAME, { recursive: true }); - this.#dhOpaque = void 0; - await this.#dhVfsParent.removeEntry(this.#dhVfsRoot.name, { recursive: true }); - this.#dhVfsRoot = this.#dhVfsParent = void 0; - } catch (e) { - sqlite3.config.error(this.vfsName, "removeVfs() failed with no recovery strategy:", e); - } - return true; - } - /** - "Pauses" this VFS by unregistering it from SQLite and - relinquishing all open SAHs, leaving the associated files - intact. If this object is already paused, this is a - no-op. Returns this object. - - This function throws if SQLite has any opened file handles - hosted by this VFS, as the alternative would be to invoke - Undefined Behavior by closing file handles out from under the - library. Similarly, automatically closing any database handles - opened by this VFS would invoke Undefined Behavior in - downstream code which is holding those pointers. - - If this function throws due to open file handles then it has - no side effects. If the OPFS API throws while closing handles - then the VFS is left in an undefined state. - - @see isPaused() - @see unpauseVfs() - */ - pauseVfs() { - if (this.#mapS3FileToOFile_.size > 0) sqlite3.SQLite3Error.toss(capi.SQLITE_MISUSE, "Cannot pause VFS", this.vfsName, "because it has opened files."); - if (this.#mapSAHToName.size > 0) { - capi.sqlite3_vfs_unregister(this.vfsName); - this.releaseAccessHandles(); - } - return this; - } - /** - Returns true if this pool is currently paused else false. - - @see pauseVfs() - @see unpauseVfs() - */ - isPaused() { - return 0 === this.#mapSAHToName.size; - } - /** - "Unpauses" this VFS, reacquiring all SAH's and (if successful) - re-registering it with SQLite. This is a no-op if the VFS is - not currently paused. - - The returned Promise resolves to this object. See - acquireAccessHandles() for how it behaves if it throws due to - SAH acquisition failure. - - @see isPaused() - @see pauseVfs() - */ - async unpauseVfs() { - if (0 === this.#mapSAHToName.size) return this.acquireAccessHandles(false).then(() => capi.sqlite3_vfs_register(this.#cVfs, 0), this); - return this; - } - //! Documented elsewhere in this file. - exportFile(name) { - const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:", name); - const n = sah.getSize() - HEADER_OFFSET_DATA; - const b = new Uint8Array(n > 0 ? n : 0); - if (n > 0) { - const nRead = sah.read(b, { at: HEADER_OFFSET_DATA }); - if (nRead != n) toss("Expected to read " + n + " bytes but read " + nRead + "."); - } - return b; - } - //! Impl for importDb() when its 2nd arg is a function. - async importDbChunked(name, callback) { - const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); - sah.truncate(0); - let nWrote = 0, chunk, checkedHeader = false; - try { - while (void 0 !== (chunk = await callback())) { - if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); - if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) { - util.affirmDbHeader(chunk); - checkedHeader = true; - } - sah.write(chunk, { at: HEADER_OFFSET_DATA + nWrote }); - nWrote += chunk.byteLength; - } - if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database."); - if (!checkedHeader) { - const header = new Uint8Array(20); - sah.read(header, { at: 0 }); - util.affirmDbHeader(header); - } - sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); - } catch (e) { - this.setAssociatedPath(sah, "", 0); - throw e; - } - this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); - return nWrote; - } - //! Documented elsewhere in this file. - importDb(name, bytes) { - if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - else if (bytes instanceof Function) return this.importDbChunked(name, bytes); - const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to."); - const n = bytes.byteLength; - if (n < 512 || n % 512 != 0) toss("Byte array size is invalid for an SQLite db."); - const header = "SQLite format 3"; - for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss("Input does not contain an SQLite database header."); - const nWrote = sah.write(bytes, { at: HEADER_OFFSET_DATA }); - if (nWrote != n) { - this.setAssociatedPath(sah, "", 0); - toss("Expected to write " + n + " bytes but wrote " + nWrote + "."); - } else { - sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 }); - this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); - } - return nWrote; - } - } - /** - A OpfsSAHPoolUtil instance is exposed to clients in order to - manipulate an OpfsSAHPool object without directly exposing that - object and allowing for some semantic changes compared to that - class. - - Class docs are in the client-level docs for - installOpfsSAHPoolVfs(). - */ - class OpfsSAHPoolUtil { - #p; - constructor(sahPool) { - this.#p = sahPool; - this.vfsName = sahPool.vfsName; - } - async addCapacity(n) { - return this.#p.addCapacity(n); - } - async reduceCapacity(n) { - return this.#p.reduceCapacity(n); - } - getCapacity() { - return this.#p.getCapacity(this.#p); - } - getFileCount() { - return this.#p.getFileCount(); - } - getFileNames() { - return this.#p.getFileNames(); - } - async reserveMinimumCapacity(min) { - const c = this.#p.getCapacity(); - return c < min ? this.#p.addCapacity(min - c) : c; - } - exportFile(name) { - return this.#p.exportFile(name); - } - importDb(name, bytes) { - return this.#p.importDb(name, bytes); - } - async wipeFiles() { - return this.#p.reset(true); - } - unlink(filename) { - return this.#p.deletePath(filename); - } - async removeVfs() { - return this.#p.removeVfs(); - } - pauseVfs() { - this.#p.pauseVfs(); - return this; - } - async unpauseVfs() { - return this.#p.unpauseVfs().then(() => this); - } - isPaused() { - return this.#p.isPaused(); - } - } - /** - Returns a resolved Promise if the current environment - has a "fully-sync" SAH impl, else a rejected Promise. - */ - const apiVersionCheck = async () => { - const dh = await navigator.storage.getDirectory(); - const fn = ".opfs-sahpool-sync-check-" + getRandomName(); - const close = (await (await dh.getFileHandle(fn, { create: true })).createSyncAccessHandle()).close(); - await close; - await dh.removeEntry(fn); - if (close?.then) toss("The local OPFS API is too old for opfs-sahpool:", "it has an async FileSystemSyncAccessHandle.close() method."); - return true; - }; - /** - installOpfsSAHPoolVfs() asynchronously initializes the OPFS - SyncAccessHandle (a.k.a. SAH) Pool VFS. It returns a Promise which - either resolves to a utility object described below or rejects with - an Error value. - - Initialization of this VFS is not automatic because its - registration requires that it lock all resources it - will potentially use, even if client code does not want - to use them. That, in turn, can lead to locking errors - when, for example, one page in a given origin has loaded - this VFS but does not use it, then another page in that - origin tries to use the VFS. If the VFS were automatically - registered, the second page would fail to load the VFS - due to OPFS locking errors. - - If this function is called more than once with a given "name" - option (see below), it will return the same Promise. Calls for - different names will return different Promises which resolve to - independent objects and refer to different VFS registrations. - - On success, the resulting Promise resolves to a utility object - which can be used to query and manipulate the pool. Its API is - described at the end of these docs. - - This function accepts an options object to configure certain - parts but it is only acknowledged for the very first call for - each distinct name and ignored for all subsequent calls with that - same name. - - The options, in alphabetical order: - - - `clearOnInit`: (default=false) if truthy, contents and filename - mapping are removed from each SAH it is acquired during - initialization of the VFS, leaving the VFS's storage in a pristine - state. Use this only for databases which need not survive a page - reload. - - - `initialCapacity`: (default=6) Specifies the default capacity of - the VFS. This should not be set unduly high because the VFS has - to open (and keep open) a file for each entry in the pool. This - setting only has an effect when the pool is initially empty. It - does not have any effect if a pool already exists. - - - `directory`: (default="."+`name`) Specifies the OPFS directory - name in which to store metadata for the `"opfs-sahpool"` - sqlite3_vfs. Only one instance of this VFS can be installed per - JavaScript engine, and any two engines with the same storage - directory name will collide with each other, leading to locking - errors and the inability to register the VFS in the second and - subsequent engine. Using a different directory name for each - application enables different engines in the same HTTP origin to - co-exist, but their data are invisible to each other. Changing - this name will effectively orphan any databases stored under - previous names. The default is unspecified but descriptive. This - option may contain multiple path elements, e.g. "foo/bar/baz", - and they are created automatically. In practice there should be - no driving need to change this. ACHTUNG: all files in this - directory are assumed to be managed by the VFS. Do not place - other files in that directory, as they may be deleted or - otherwise modified by the VFS. - - - `name`: (default="opfs-sahpool") sets the name to register this - VFS under. Normally this should not be changed, but it is - possible to register this VFS under multiple names so long as - each has its own separate directory to work from. The storage for - each is invisible to all others. The name must be a string - compatible with `sqlite3_vfs_register()` and friends and suitable - for use in URI-style database file names. - - Achtung: if a custom `name` is provided, a custom `directory` - must also be provided if any other instance is registered with - the default directory. If no directory is explicitly provided - then a directory name is synthesized from the `name` option. - - - - `forceReinitIfPreviouslyFailed`: (default=`false`) Is a fallback option - to assist in working around certain flaky environments which may - mysteriously fail to permit access to OPFS sync access handles on - an initial attempt but permit it on a second attemp. This option - should never be used but is provided for those who choose to - throw caution to the wind and trust such environments. If this - option is truthy _and_ the previous attempt to initialize this - VFS with the same `name` failed, the VFS will attempt to - initialize a second time instead of returning the cached - failure. See discussion at: - - - - Peculiarities of this VFS vis a vis other SQLite VFSes: - - - Paths given to it _must_ be absolute. Relative paths will not - be properly recognized. This is arguably a bug but correcting it - requires some hoop-jumping in routines which have no business - doing such tricks. (2026-01-19 (2.5 years later): the specifics - are lost to history, but this was a side effect of xOpen() - receiving an immutable C-string filename, to which no implicit - "/" can be prefixed without causing a discrepancy between what - the user provided and what the VFS stores. Its conceivable that - that quirk could be glossed over in xFullPathname(), but - regressions when doing so cannot be ruled out, so there are no - current plans to change this behavior.) - - - It is possible to install multiple instances under different - names, each sandboxed from one another inside their own private - directory. This feature exists primarily as a way for disparate - applications within a given HTTP origin to use this VFS without - introducing locking issues between them. - - - The API for the utility object passed on by this function's - Promise, in alphabetical order... - - - [async] number addCapacity(n) - - Adds `n` entries to the current pool. This change is persistent - across sessions so should not be called automatically at each app - startup (but see `reserveMinimumCapacity()`). Its returned Promise - resolves to the new capacity. Because this operation is necessarily - asynchronous, the C-level VFS API cannot call this on its own as - needed. - - - byteArray exportFile(name) - - Synchronously reads the contents of the given file into a Uint8Array - and returns it. This will throw if the given name is not currently - in active use or on I/O error. Note that the given name is _not_ - visible directly in OPFS (or, if it is, it's not from this VFS). - - - number getCapacity() - - Returns the number of files currently contained - in the SAH pool. The default capacity is only large enough for one - or two databases and their associated temp files. - - - number getFileCount() - - Returns the number of files from the pool currently allocated to - slots. This is not the same as the files being "opened". - - - array getFileNames() - - Returns an array of the names of the files currently allocated to - slots. This list is the same length as getFileCount(). - - - void importDb(name, bytes) - - Imports the contents of an SQLite database, provided as a byte - array or ArrayBuffer, under the given name, overwriting any - existing content. Throws if the pool has no available file slots, - on I/O error, or if the input does not appear to be a - database. In the latter case, only a cursory examination is made. - Results are undefined if the given db name refers to an opened - db. Note that this routine is _only_ for importing database - files, not arbitrary files, the reason being that this VFS will - automatically clean up any non-database files so importing them - is pointless. - - If passed a function for its second argument, its behavior - changes to asynchronous and it imports its data in chunks fed to - it by the given callback function. It calls the callback (which - may be async) repeatedly, expecting either a Uint8Array or - ArrayBuffer (to denote new input) or undefined (to denote - EOF). For so long as the callback continues to return - non-undefined, it will append incoming data to the given - VFS-hosted database file. The result of the resolved Promise when - called this way is the size of the resulting database. - - On success this routine rewrites the database header bytes in the - output file (not the input array) to force disabling of WAL mode. - - On a write error, the handle is removed from the pool and made - available for re-use. - - - [async] number reduceCapacity(n) - - Removes up to `n` entries from the pool, with the caveat that it can - only remove currently-unused entries. It returns a Promise which - resolves to the number of entries actually removed. - - - [async] boolean removeVfs() - - Unregisters the opfs-sahpool VFS and removes its directory from OPFS - (which means that _all client content_ is removed). After calling - this, the VFS may no longer be used and there is no way to re-add it - aside from reloading the current JavaScript context. - - Results are undefined if a database is currently in use with this - VFS. - - The returned Promise resolves to true if it performed the removal - and false if the VFS was not installed. - - If the VFS has a multi-level directory, e.g. "/foo/bar/baz", _only_ - the bottom-most directory is removed because this VFS cannot know for - certain whether the higher-level directories contain data which - should be removed. - - - [async] number reserveMinimumCapacity(min) - - If the current capacity is less than `min`, the capacity is - increased to `min`, else this returns with no side effects. The - resulting Promise resolves to the new capacity. - - - boolean unlink(filename) - - If a virtual file exists with the given name, disassociates it from - the pool and returns true, else returns false without side - effects. Results are undefined if the file is currently in active - use. - - - string vfsName - - The SQLite VFS name under which this pool's VFS is registered. - - - [async] void wipeFiles() - - Clears all client-defined state of all SAHs and makes all of them - available for re-use by the pool. Results are undefined if any such - handles are currently in use, e.g. by an sqlite3 db. - - APIs specific to the "pause" capability (added in version 3.49): - - Summary: "pausing" the VFS disassociates it from SQLite and - relinquishes its SAHs so that they may be opened by another - instance of this VFS (running in a separate tab/page or Worker). - "Unpausing" it takes back control, if able. - - - pauseVfs() - - "Pauses" this VFS by unregistering it from SQLite and - relinquishing all open SAHs, leaving the associated files intact. - This enables pages/tabs to coordinate semi-concurrent usage of - this VFS. If this object is already paused, this is a - no-op. Returns this object. Throws if SQLite has any opened file - handles hosted by this VFS. If this function throws due to open - file handles then it has no side effects. If the OPFS API throws - while closing handles then the VFS is left in an undefined state. - - - isPaused() - - Returns true if this VFS is paused, else false. - - - [async] unpauseVfs() - - Restores the VFS to an active state after having called - pauseVfs() on it. This is a no-op if the VFS is not paused. The - returned Promise resolves to this object on success. A rejected - Promise means there was a problem reacquiring the SAH handles - (possibly because they're in use by another instance or have - since been removed). Generically speaking, there is no recovery - strategy for that type of error, but if the problem is simply - that the OPFS files are locked, then a later attempt to unpause - it, made after the concurrent instance releases the SAHs, may - recover from the situation. - */ - sqlite3.installOpfsSAHPoolVfs = async function(options = Object.create(null)) { - options = Object.assign(Object.create(null), optionDefaults, options || {}); - const vfsName = options.name; - if (options.$testThrowPhase1) throw options.$testThrowPhase1; - if (initPromises[vfsName]) try { - return await initPromises[vfsName]; - } catch (e) { - if (options.forceReinitIfPreviouslyFailed) delete initPromises[vfsName]; - else throw e; - } - if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return initPromises[vfsName] = Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs.")); - /** - Maintenance reminder: the order of ASYNC ops in this function - is significant. We need to have them all chained at the very - end in order to be able to catch a race condition where - installOpfsSAHPoolVfs() is called twice in rapid succession, - e.g.: - - installOpfsSAHPoolVfs().then(console.warn.bind(console)); - installOpfsSAHPoolVfs().then(console.warn.bind(console)); - - If the timing of the async calls is not "just right" then that - second call can end up triggering the init a second time and chaos - ensues. - */ - return initPromises[vfsName] = apiVersionCheck().then(async function() { - if (options.$testThrowPhase2) throw options.$testThrowPhase2; - const thePool = new OpfsSAHPool(options); - return thePool.isReady.then(async () => { - /** The poolUtil object will be the result of the - resolved Promise. */ - const poolUtil = new OpfsSAHPoolUtil(thePool); - if (sqlite3.oo1) { - const oo1 = sqlite3.oo1; - const theVfs = thePool.getVfs(); - const OpfsSAHPoolDb = function(...args) { - const opt = oo1.DB.dbCtorHelper.normalizeArgs(...args); - opt.vfs = theVfs.$zName; - oo1.DB.dbCtorHelper.call(this, opt); - }; - OpfsSAHPoolDb.prototype = Object.create(oo1.DB.prototype); - poolUtil.OpfsSAHPoolDb = OpfsSAHPoolDb; - } - thePool.log("VFS initialized."); - return poolUtil; - }).catch(async (e) => { - await thePool.removeVfs().catch(() => {}); - throw e; - }); - }).catch((err) => { - return initPromises[vfsName] = Promise.reject(err); - }); - }; - }); - try { - const bootstrapConfig = Object.assign( - Object.create(null), - /** The WASM-environment-dependent configuration for sqlite3ApiBootstrap() */ - { - memory: "undefined" !== typeof wasmMemory ? wasmMemory : EmscriptenModule["wasmMemory"], - exports: "undefined" !== typeof wasmExports ? wasmExports : Object.prototype.hasOwnProperty.call(EmscriptenModule, "wasmExports") ? EmscriptenModule["wasmExports"] : EmscriptenModule["asm"] - }, - globalThis.sqlite3ApiBootstrap.defaultConfig, - globalThis.sqlite3ApiConfig || {} - ); - sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig); - /** - For purposes of the Emscripten build, call sqlite3ApiBootstrap(). - Ideally clients should be able to inject their own config here, - but that's not practical in this particular build constellation - because of the order everything happens in. Clients may either - define globalThis.sqlite3ApiConfig or modify - globalThis.sqlite3ApiBootstrap.defaultConfig to tweak the default - configuration used by a no-args call to sqlite3ApiBootstrap(), - but must have first loaded their WASM module in order to be able - to provide the necessary configuration state. - */ - const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig); - delete globalThis.sqlite3ApiBootstrap; - return p; - } catch (e) { - console.error("sqlite3ApiBootstrap() error:", e); - throw e; - } - }; - if (runtimeInitialized) moduleRtn = Module; - else moduleRtn = new Promise((resolve, reject) => { - readyPromiseResolve = resolve; - readyPromiseReject = reject; - }); - return moduleRtn; -} -sqlite3InitModule = (function() { - /** - In order to hide the sqlite3InitModule()'s resulting - Emscripten module from downstream clients (and simplify our - documentation by being able to elide those details), we hide that - function and expose a hand-written sqlite3InitModule() to return - the sqlite3 object (most of the time). - */ - const originalInit = sqlite3InitModule; - if (!originalInit) throw new Error("Expecting sqlite3InitModule to be defined by the Emscripten build."); - /** - We need to add some state which our custom Module.locateFile() - can see, but an Emscripten limitation currently prevents us from - attaching it to the sqlite3InitModule function object: - - https://github.com/emscripten-core/emscripten/issues/18071 - - The only(?) current workaround is to temporarily stash this state - into the global scope and delete it when sqlite3InitModule() - is called. - */ - const sIMS = globalThis.sqlite3InitModuleState = Object.assign(Object.create(null), { - moduleScript: globalThis?.document?.currentScript, - isWorker: "undefined" !== typeof WorkerGlobalScope, - location: globalThis.location, - urlParams: globalThis?.location?.href ? new URL(globalThis.location.href).searchParams : new URLSearchParams(), - wasmFilename: "sqlite3.wasm" - }); - sIMS.debugModule = sIMS.urlParams.has("sqlite3.debugModule") ? (...args) => console.warn("sqlite3.debugModule:", ...args) : () => {}; - if (sIMS.urlParams.has("sqlite3.dir")) sIMS.sqlite3Dir = sIMS.urlParams.get("sqlite3.dir") + "/"; - else if (sIMS.moduleScript) { - const li = sIMS.moduleScript.src.split("/"); - li.pop(); - sIMS.sqlite3Dir = li.join("/") + "/"; - } - const sIM = globalThis.sqlite3InitModule = function ff(...args) { - sIMS.emscriptenLocateFile = args[0]?.locateFile; - sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm; - return originalInit(...args).then((EmscriptenModule) => { - sIMS.debugModule("sqlite3InitModule() sIMS =", sIMS); - sIMS.debugModule("sqlite3InitModule() EmscriptenModule =", EmscriptenModule); - const s = EmscriptenModule.runSQLite3PostLoadInit(sIMS, EmscriptenModule, !!ff.__isUnderTest); - sIMS.debugModule("sqlite3InitModule() sqlite3 =", s); - return s; - }).catch((e) => { - console.error("Exception loading sqlite3 module:", e); - throw e; - }); - }; - sIM.ready = originalInit.ready; - if (sIMS.moduleScript) { - let src = sIMS.moduleScript.src.split("/"); - src.pop(); - sIMS.scriptDir = src.join("/") + "/"; - } - sIMS.debugModule("extern-post-js.c-pp.js sqlite3InitModuleState =", sIMS); - return sIM; -})(); -//#endregion -//#region src/bin/sqlite3-worker1.mjs -sqlite3InitModule().then((sqlite3) => sqlite3.initWorker1API()); -//#endregion -export {}; diff --git a/src/web/sqlite-wasm/sqlite3.wasm b/src/web/sqlite-wasm/sqlite3.wasm deleted file mode 100644 index 28b092090992a3db2b84c5e66094629503135b07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859730 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