UNPKG

@sqlite.org/sqlite-wasm

Version:

SQLite Wasm conveniently wrapped as an ES Module.

1,603 lines (1,582 loc) 579 kB
//#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.53.0" ** SQLITE_VERSION_NUMBER 3053000 ** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** ** Emscripten SDK: 5.0.5 */ 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; var readyPromiseResolve, readyPromiseReject; var runtimeInitialized = false; function updateMemoryViews() { var b = wasmMemory.buffer; HEAP8 = new Int8Array(b); HEAP16 = new Int16Array(b); HEAPU8 = new Uint8Array(b); HEAPU16 = new Uint16Array(b); HEAP32 = new Int32Array(b); HEAPU32 = new Uint32Array(b); HEAPF32 = new Float32Array(b); HEAPF64 = new Float64Array(b); HEAP64 = new BigInt64Array(b); HEAPU64 = 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 * @noreturn */ 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)); } /** @type {!Int16Array} */ var HEAP16; /** @type {!Int32Array} */ var HEAP32; /** not-@type {!BigInt64Array} */ var HEAP64; /** @type {!Int8Array} */ var HEAP8; /** @type {!Float32Array} */ var HEAPF32; /** @type {!Float64Array} */ var HEAPF64; /** @type {!Uint16Array} */ var HEAPU16; /** @type {!Uint32Array} */ var HEAPU32; /** not-@type {!BigUint64Array} */ var HEAPU64; /** @type {!Uint8Array} */ var HEAPU8; 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), 0); }; 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 = MEMFS.emptyFileContents ??= new Uint8Array(0); } 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) { return node.contents.subarray(0, node.usedBytes); }, expandFileStorage(node, newCapacity) { var prevCapacity = node.contents.length; if (prevCapacity >= newCapacity) return; newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 0); if (prevCapacity) newCapacity = Math.max(newCapacity, 256); var oldContents = MEMFS.getFileDataAsTypedArray(node); node.contents = new Uint8Array(newCapacity); node.contents.set(oldContents); }, resizeFileStorage(node, newSize) { if (node.usedBytes == newSize) return; var oldContents = node.contents; node.contents = new Uint8Array(newSize); 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 = "<generic error, no 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); buffer.set(contents.subarray(position, position + size), offset); 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 (canOwn) { node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; } else { MEMFS.expandFileStorage(node, position + length); node.contents.set(buffer.subarray(offset, offset + length), position); 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.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) => { if (typeof str != "string") return 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_fileDataToTypedArray = (data) => { if (typeof data == "string") data = intArrayFromString(data, true); if (!data.subarray) data = new Uint8Array(data); return data; }; 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, 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; if (perms.includes("w") && !(node.mode & 146)) return 2; 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; var mode = FS.flagsToPermissionString(flags); if (FS.isDir(node.mode)) { if (mode !== "r" || flags & 576) return 31; } return FS.nodePermissions(node, mode); }, 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); errC