├── .gitignore ├── .prettierrc ├── README.md ├── bin └── index.js ├── demo ├── index.html ├── main-thread.js ├── module-workers-polyfill.min.js ├── script.js ├── worker.js └── wrapped-worker.js ├── index.d.ts ├── index.mjs ├── package-lock.json ├── package.json └── sqlite-wasm ├── .gitignore └── jswasm ├── sqlite3-bundler-friendly.mjs ├── sqlite3-node.mjs ├── sqlite3-opfs-async-proxy.js ├── sqlite3-worker1-bundler-friendly.mjs ├── sqlite3-worker1-promiser-bundler-friendly.js ├── sqlite3-worker1-promiser.js ├── sqlite3-worker1.js ├── sqlite3.js ├── sqlite3.mjs └── sqlite3.wasm /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # Compiled output 3 | /dist 4 | /tmp 5 | /out-tsc 6 | /bazel-out 7 | 8 | # Node 9 | /node_modules 10 | npm-debug.log 11 | yarn-error.log 12 | 13 | # IDEs and editors 14 | .idea/ 15 | .project 16 | .classpath 17 | .c9/ 18 | *.launch 19 | .settings/ 20 | *.sublime-workspace 21 | 22 | # Visual Studio Code 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | .history/* 29 | 30 | # Miscellaneous 31 | /connect.lock 32 | /coverage 33 | /libpeerconnection.log 34 | testem.log 35 | /typings 36 | 37 | # System files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | node_modules/* 42 | dist/ 43 | bundle -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "embeddedLanguageFormatting": "auto", 5 | "htmlWhitespaceSensitivity": "css", 6 | "insertPragma": false, 7 | "bracketSameLine": false, 8 | "jsxSingleQuote": false, 9 | "printWidth": 80, 10 | "proseWrap": "always", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleQuote": true, 15 | "tabWidth": 2, 16 | "trailingComma": "all", 17 | "useTabs": false, 18 | "vueIndentScriptAndStyle": false, 19 | "plugins": ["prettier-plugin-jsdoc"] 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > NOTE This fork enables the SQLite [bytecode](byte) and 2 | > [session](https://www.sqlite.org/sessionintro.html) extension. 3 | 4 | ## Changes 5 | 6 | - Adds Node compatibility to regular entry point: 7 | 8 | ```ts 9 | function instantiateAsync(binary, binaryFile, imports, callback) { 10 | if ( 11 | !binary && 12 | typeof WebAssembly.instantiateStreaming == 'function' && 13 | !isDataURI(binaryFile) && 14 | typeof fetch == 'function' 15 | ) { 16 | // added in this fork 17 | const isNode = 18 | typeof process !== 'undefined' && 19 | process.versions != null && 20 | process.versions.node != null; 21 | if (isNode) { 22 | return import('fs').then((fs) => { 23 | const buffer = fs.readFileSync(binaryFile.replace('file://', '')); 24 | const bytes = new Uint8Array(buffer); 25 | return WebAssembly.instantiate(bytes, imports).then(callback); 26 | }); 27 | } 28 | 29 | return fetch(binaryFile, { credentials: 'same-origin' }).then( 30 | // ... 31 | 32 | ``` 33 | 34 | --- 35 | 36 | # SQLite Wasm 37 | 38 | SQLite Wasm conveniently wrapped as an ES Module. 39 | 40 | > **Warning** 41 | > 42 | > This project wraps the code of 43 | > [SQLite Wasm](https://sqlite.org/wasm/doc/trunk/index.md) with _no_ changes, 44 | > apart from added TypeScript types. Please do _not_ file issues or feature 45 | > requests regarding the underlying SQLite Wasm code here. Instead, please 46 | > follow the 47 | > [SQLite bug filing instructions](https://www.sqlite.org/src/wiki?name=Bug+Reports). 48 | > Filing TypeScript type related issues and feature requests is fine. 49 | 50 | ## Installation 51 | 52 | ```bash 53 | npm install @sqlite.org/sqlite-wasm 54 | ``` 55 | 56 | ## Usage 57 | 58 | There are three ways to use SQLite Wasm: 59 | 60 | - [in the main thread with a wrapped worker](#in-a-wrapped-worker-with-opfs-if-available) 61 | (🏆 preferred option) 62 | - [in a worker](#in-a-worker-with-opfs-if-available) 63 | - [in the main thread](#in-the-main-thread-without-opfs) 64 | 65 | Only the worker versions allow you to use the origin private file system (OPFS) 66 | storage back-end. 67 | 68 | ### In a wrapped worker (with OPFS if available): 69 | 70 | > **Warning** 71 | > 72 | > For this to work, you need to set the following headers on your server: 73 | > 74 | > `Cross-Origin-Opener-Policy: same-origin` 75 | > 76 | > `Cross-Origin-Embedder-Policy: require-corp` 77 | 78 | ```js 79 | import { sqlite3Worker1Promiser } from '@sqlite.org/sqlite-wasm'; 80 | 81 | const log = (...args) => console.log(...args); 82 | const error = (...args) => console.error(...args); 83 | 84 | (async () => { 85 | try { 86 | log('Loading and initializing SQLite3 module...'); 87 | 88 | const promiser = await new Promise((resolve) => { 89 | const _promiser = sqlite3Worker1Promiser({ 90 | onready: () => { 91 | resolve(_promiser); 92 | }, 93 | }); 94 | }); 95 | 96 | log('Done initializing. Running demo...'); 97 | 98 | let response; 99 | 100 | response = await promiser('config-get', {}); 101 | log('Running SQLite3 version', response.result.version.libVersion); 102 | 103 | response = await promiser('open', { 104 | filename: 'file:mydb.sqlite3?vfs=opfs', 105 | }); 106 | const { dbId } = response; 107 | log( 108 | 'OPFS is available, created persisted database at', 109 | response.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'), 110 | ); 111 | // Your SQLite code here. 112 | } catch (err) { 113 | if (!(err instanceof Error)) { 114 | err = new Error(err.result.message); 115 | } 116 | error(err.name, err.message); 117 | } 118 | })(); 119 | ``` 120 | 121 | The `promiser` object above implements the 122 | [Worker1 API](https://sqlite.org/wasm/doc/trunk/api-worker1.md#worker1-methods). 123 | 124 | ### In a worker (with OPFS if available): 125 | 126 | > **Warning** 127 | > 128 | > For this to work, you need to set the following headers on your server: 129 | > 130 | > `Cross-Origin-Opener-Policy: same-origin` 131 | > 132 | > `Cross-Origin-Embedder-Policy: require-corp` 133 | 134 | ```js 135 | // In `main.js`. 136 | const worker = new Worker('worker.js', { type: 'module' }); 137 | ``` 138 | 139 | ```js 140 | // In `worker.js`. 141 | import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; 142 | 143 | const log = (...args) => console.log(...args); 144 | const error = (...args) => console.error(...args); 145 | 146 | const start = function (sqlite3) { 147 | log('Running SQLite3 version', sqlite3.version.libVersion); 148 | let db; 149 | if ('opfs' in sqlite3) { 150 | db = new sqlite3.oo1.OpfsDb('/mydb.sqlite3'); 151 | log('OPFS is available, created persisted database at', db.filename); 152 | } else { 153 | db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct'); 154 | log('OPFS is not available, created transient database', db.filename); 155 | } 156 | // Your SQLite code here. 157 | }; 158 | 159 | log('Loading and initializing SQLite3 module...'); 160 | sqlite3InitModule({ 161 | print: log, 162 | printErr: error, 163 | }).then((sqlite3) => { 164 | log('Done initializing. Running demo...'); 165 | try { 166 | start(sqlite3); 167 | } catch (err) { 168 | error(err.name, err.message); 169 | } 170 | }); 171 | ``` 172 | 173 | The `db` object above implements the 174 | [Object Oriented API #1](https://sqlite.org/wasm/doc/trunk/api-oo1.md). 175 | 176 | ### In the main thread (without OPFS): 177 | 178 | ```js 179 | import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; 180 | 181 | const log = (...args) => console.log(...args); 182 | const error = (...args) => console.error(...args); 183 | 184 | const start = function (sqlite3) { 185 | log('Running SQLite3 version', sqlite3.version.libVersion); 186 | const db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct'); 187 | // Your SQLite code here. 188 | }; 189 | 190 | log('Loading and initializing SQLite3 module...'); 191 | sqlite3InitModule({ 192 | print: log, 193 | printErr: error, 194 | }).then((sqlite3) => { 195 | try { 196 | log('Done initializing. Running demo...'); 197 | start(sqlite3); 198 | } catch (err) { 199 | error(err.name, err.message); 200 | } 201 | }); 202 | ``` 203 | 204 | The `db` object above implements the 205 | [Object Oriented API #1](https://sqlite.org/wasm/doc/trunk/api-oo1.md). 206 | 207 | ## Usage with vite 208 | 209 | If you are using [vite](https://vitejs.dev/), you need to add the following 210 | config option in `vite.config.js`: 211 | 212 | ```js 213 | import { defineConfig } from 'vite'; 214 | 215 | export default defineConfig({ 216 | server: { 217 | headers: { 218 | 'Cross-Origin-Opener-Policy': 'same-origin', 219 | 'Cross-Origin-Embedder-Policy': 'require-corp', 220 | }, 221 | }, 222 | optimizeDeps: { 223 | exclude: ['@sqlite.org/sqlite-wasm'], 224 | }, 225 | }); 226 | ``` 227 | 228 | Check out a 229 | [sample project](https://stackblitz.com/edit/vitejs-vite-ttrbwh?file=main.js) 230 | that shows this in action. 231 | 232 | ## Demo 233 | 234 | See the [demo](https://github.com/sqlite/sqlite-wasm/tree/main/demo) folder for 235 | examples of how to use this in the main thread and in a worker. (Note that the 236 | worker variant requires special HTTP headers, so it can't be hosted on GitHub 237 | Pages.) An example that shows how to use this with vite is available on 238 | [StackBlitz](https://stackblitz.com/edit/vitejs-vite-ttrbwh?file=main.js). 239 | 240 | ## Projects using this package 241 | 242 | See the list of 243 | [npm dependents](https://www.npmjs.com/browse/depended/@sqlite.org/sqlite-wasm) 244 | for this package. 245 | 246 | ## Deploying a new version 247 | 248 | (These steps can only be executed by maintainers.) 249 | 250 | 1. Update the version number in `package.json` reflecting the current 251 | [SQLite version number](https://sqlite.org/download.html) and add a build 252 | identifier suffix like `-build1`. The complete version number should read 253 | something like `3.41.2-build1`. 254 | 1. Run `npm run build` to build the ES Module. This downloads the latest SQLite 255 | Wasm binary and builds the ES Module. 256 | 1. Run `npm run deploy` to commit the changes, push to GitHub, and publish the 257 | new version to npm. 258 | 259 | ## License 260 | 261 | Apache 2.0. 262 | 263 | ## Acknowledgements 264 | 265 | This project is based on [SQLite Wasm](https://sqlite.org/wasm), which it 266 | conveniently wraps as an ES Module and publishes to npm as 267 | [`@sqlite.org/sqlite-wasm`](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm). 268 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import fetch from 'node-fetch'; 3 | import decompress from 'decompress'; 4 | 5 | async function getSqliteWasmDownloadLink() { 6 | const response = await fetch('https://sqlite.org/download.html'); 7 | const html = await response.text(); 8 | const sqliteWasmLink = 9 | 'https://sqlite.org/' + 10 | html 11 | .replace( 12 | /^.*?.*?$/gms, 13 | '$1', 14 | ) 15 | .split(/\n/) 16 | .filter((row) => /sqlite-wasm/.test(row))[0] 17 | .split(/,/)[2]; 18 | console.log(`Found SQLite Wasm download link: ${sqliteWasmLink}`); 19 | return sqliteWasmLink; 20 | } 21 | 22 | async function downloadAndUnzipSqliteWasm(sqliteWasmDownloadLink) { 23 | if (!sqliteWasmDownloadLink) { 24 | throw new Error('Unable to find SQLite Wasm download link'); 25 | } 26 | console.log('Downloading and unzipping SQLite Wasm...'); 27 | const response = await fetch(sqliteWasmDownloadLink); 28 | if (!response.ok || response.status !== 200) { 29 | throw new Error( 30 | `Unable to download SQLite Wasm from ${sqliteWasmDownloadLink}`, 31 | ); 32 | } 33 | const buffer = await response.arrayBuffer(); 34 | fs.writeFileSync('sqlite-wasm.zip', Buffer.from(buffer)); 35 | const files = await decompress('sqlite-wasm.zip', 'sqlite-wasm', { 36 | strip: 1, 37 | filter: (file) => 38 | /jswasm/.test(file.path) && /(\.mjs|\.wasm|\.js)$/.test(file.path), 39 | }); 40 | console.log( 41 | `Downloaded and unzipped:\n${files 42 | .map((file) => (/\//.test(file.path) ? '‣ ' + file.path + '\n' : '')) 43 | .join('')}`, 44 | ); 45 | fs.rmSync('sqlite-wasm.zip'); 46 | } 47 | 48 | async function main() { 49 | try { 50 | const sqliteWasmLink = await getSqliteWasmDownloadLink(); 51 | await downloadAndUnzipSqliteWasm(sqliteWasmLink); 52 | fs.copyFileSync( 53 | './node_modules/module-workers-polyfill/module-workers-polyfill.min.js', 54 | './demo/module-workers-polyfill.min.js', 55 | ); 56 | } catch (err) { 57 | console.error(err.name, err.message); 58 | } 59 | } 60 | 61 | main(); 62 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SQLite Wasm Demo 6 | 7 | 8 | 9 | 10 | 11 |

SQLite Wasm Demo

12 |

Wrapped Worker

13 |
14 |

Worker

15 |
16 |

Main thread

17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/main-thread.js: -------------------------------------------------------------------------------- 1 | import sqlite3InitModule from '../index.mjs'; 2 | 3 | const container = document.querySelector('.main-thread'); 4 | 5 | const logHtml = function (cssClass, ...args) { 6 | const div = document.createElement('div'); 7 | if (cssClass) div.classList.add(cssClass); 8 | div.append(document.createTextNode(args.join(' '))); 9 | container.append(div); 10 | }; 11 | 12 | const log = (...args) => logHtml('', ...args); 13 | const error = (...args) => logHtml('error', ...args); 14 | 15 | const start = function (sqlite3) { 16 | log('Running SQLite3 version', sqlite3.version.libVersion); 17 | const db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct'); 18 | log('Created transient database', db.filename); 19 | 20 | try { 21 | log('Creating a table...'); 22 | db.exec('CREATE TABLE IF NOT EXISTS t(a,b)'); 23 | log('Insert some data using exec()...'); 24 | for (let i = 20; i <= 25; ++i) { 25 | db.exec({ 26 | sql: 'INSERT INTO t(a,b) VALUES (?,?)', 27 | bind: [i, i * 2], 28 | }); 29 | } 30 | log('Query data with exec()...'); 31 | db.exec({ 32 | sql: 'SELECT a FROM t ORDER BY a LIMIT 3', 33 | callback: (row) => { 34 | log(row); 35 | }, 36 | }); 37 | } finally { 38 | db.close(); 39 | } 40 | }; 41 | 42 | log('Loading and initializing SQLite3 module...'); 43 | sqlite3InitModule({ 44 | print: log, 45 | printErr: error, 46 | }).then((sqlite3) => { 47 | log('Done initializing. Running demo...'); 48 | try { 49 | start(sqlite3); 50 | } catch (err) { 51 | error(err.name, err.message); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /demo/module-workers-polyfill.min.js: -------------------------------------------------------------------------------- 1 | !(function (e) { 2 | if (!e || !0 !== e._$P) { 3 | if (e) { 4 | var n, 5 | r = Object.defineProperty({}, 'type', { 6 | get: function () { 7 | n = !0; 8 | }, 9 | }); 10 | try { 11 | var t = URL.createObjectURL( 12 | new Blob([''], { type: 'text/javascript' }), 13 | ); 14 | new e(t, r).terminate(), URL.revokeObjectURL(t); 15 | } catch (e) {} 16 | if (!n) 17 | try { 18 | new e('data:text/javascript,', r).terminate(); 19 | } catch (e) {} 20 | if (n) return; 21 | (self.Worker = function (n, r) { 22 | return ( 23 | r && 24 | 'module' == r.type && 25 | ((r = { name: n + '\n' + (r.name || '') }), 26 | (n = 27 | 'undefined' == typeof document 28 | ? location.href 29 | : (document.currentScript && document.currentScript.src) || 30 | new Error().stack.match( 31 | /[(@]((file|https?):\/\/[^)]+?):\d+(:\d+)?(?:\)|$)/m, 32 | )[1])), 33 | new e(n, r) 34 | ); 35 | })._$P = !0; 36 | } 37 | 'undefined' == typeof document && 38 | (function () { 39 | var e = {}, 40 | n = {}; 41 | function r(e, n) { 42 | for ( 43 | n = n.replace(/^(\.\.\/|\.\/)/, e.replace(/[^/]+$/g, '') + '$1'); 44 | n !== (n = n.replace(/[^/]+\/\.\.\//g, '')); 45 | 46 | ); 47 | return n.replace(/\.\//g, ''); 48 | } 49 | var t = [], 50 | s = t.push.bind(t); 51 | addEventListener('message', s); 52 | var a = self.name.match(/^[^\n]+/)[0]; 53 | (self.name = self.name.replace(/^[^\n]*\n/g, '')), 54 | (function t(s, a) { 55 | var u, 56 | o = s; 57 | return ( 58 | a && (s = r(a, s)), 59 | e[s] || 60 | (e[s] = fetch(s).then(function (a) { 61 | if ((o = a.url) !== s) { 62 | if (null != e[o]) return e[o]; 63 | e[o] = e[s]; 64 | } 65 | return a.text().then(function (e) { 66 | if (!a.ok) throw e; 67 | var c = { exports: {} }; 68 | u = n[o] || (n[o] = c.exports); 69 | var i = function (e) { 70 | return t(e, o); 71 | }, 72 | f = []; 73 | return ( 74 | (e = (function (e, n) { 75 | n = n || []; 76 | var r, 77 | t = [], 78 | a = 0; 79 | function u(e, n) { 80 | for ( 81 | var s, 82 | a = /(?:^|,)\s*([\w$]+)(?:\s+as\s+([\w$]+))?\s*/g, 83 | u = []; 84 | (s = a.exec(e)); 85 | 86 | ) 87 | n 88 | ? t.push((s[2] || s[1]) + ':' + s[1]) 89 | : u.push((s[2] || s[1]) + '=' + r + '.' + s[1]); 90 | return u; 91 | } 92 | return ( 93 | (e = e 94 | .replace( 95 | /(^\s*|[;}\s\n]\s*)import\s*(?:(?:([\w$]+)(?:\s*\,\s*\{([^}]+)\})?|(?:\*\s*as\s+([\w$]+))|\{([^}]*)\})\s*from)?\s*(['"])(.+?)\6/g, 96 | function (e, t, s, o, c, i, f, p) { 97 | return ( 98 | n.push(p), 99 | (t += 100 | 'var ' + 101 | (r = '$im$' + ++a) + 102 | '=$require(' + 103 | f + 104 | p + 105 | f + 106 | ')'), 107 | s && 108 | (t += 109 | ';var ' + 110 | s + 111 | " = 'default' in " + 112 | r + 113 | ' ? ' + 114 | r + 115 | '.default : ' + 116 | r), 117 | c && (t += ';var ' + c + ' = ' + r), 118 | (o = o || i) && (t += ';var ' + u(o, !1)), 119 | t 120 | ); 121 | }, 122 | ) 123 | .replace( 124 | /((?:^|[;}\s\n])\s*)export\s*(?:\s+(default)\s+|((?:async\s+)?function\s*\*?|class|const\s|let\s|var\s)\s*([a-zA-Z0-9$_{[]+))/g, 125 | function (e, n, r, s, u) { 126 | if (r) { 127 | var o = '$im$' + ++a; 128 | return ( 129 | t.push('default:' + o), n + 'var ' + o + '=' 130 | ); 131 | } 132 | return t.push(u + ':' + u), n + s + ' ' + u; 133 | }, 134 | ) 135 | .replace( 136 | /((?:^|[;}\s\n])\s*)export\s*\{([^}]+)\}\s*;?/g, 137 | function (e, n, r) { 138 | return u(r, !0), n; 139 | }, 140 | ) 141 | .replace( 142 | /((?:^|[^a-zA-Z0-9$_@`'".])\s*)(import\s*\([\s\S]+?\))/g, 143 | '$1$$$2', 144 | )).replace( 145 | /((?:^|[^a-zA-Z0-9$_@`'".])\s*)import\.meta\.url/g, 146 | '$1' + JSON.stringify(s), 147 | ) + 148 | '\n$module.exports={' + 149 | t.join(',') + 150 | '}' 151 | ); 152 | })(e, f)), 153 | Promise.all( 154 | f.map(function (e) { 155 | var s = r(o, e); 156 | return s in n ? n[s] : t(s); 157 | }), 158 | ).then(function (n) { 159 | e += '\n//# sourceURL=' + s; 160 | try { 161 | var r = new Function( 162 | '$import', 163 | '$require', 164 | '$module', 165 | '$exports', 166 | e, 167 | ); 168 | } catch (n) { 169 | var t = n.line - 1, 170 | a = n.column, 171 | o = e.split('\n'), 172 | p = 173 | (o[t - 2] || '') + 174 | '\n' + 175 | o[t - 1] + 176 | '\n' + 177 | (null == a 178 | ? '' 179 | : new Array(a).join('-') + '^\n') + 180 | (o[t] || ''), 181 | l = new Error(n.message + '\n\n' + p, s, t); 182 | throw ( 183 | ((l.sourceURL = l.fileName = s), 184 | (l.line = t), 185 | (l.column = a), 186 | l) 187 | ); 188 | } 189 | var m = r( 190 | i, 191 | function (e) { 192 | return n[f.indexOf(e)]; 193 | }, 194 | c, 195 | c.exports, 196 | ); 197 | return ( 198 | null != m && (c.exports = m), 199 | Object.assign(u, c.exports), 200 | c.exports 201 | ); 202 | }) 203 | ); 204 | }); 205 | })) 206 | ); 207 | })(a) 208 | .then(function () { 209 | removeEventListener('message', s), t.map(dispatchEvent); 210 | }) 211 | .catch(function (e) { 212 | setTimeout(function () { 213 | throw e; 214 | }); 215 | }); 216 | })(); 217 | } 218 | })(self.Worker); 219 | -------------------------------------------------------------------------------- /demo/script.js: -------------------------------------------------------------------------------- 1 | const container = document.querySelector('.worker'); 2 | 3 | const logHtml = (cssClass, ...args) => { 4 | const div = document.createElement('div'); 5 | if (cssClass) div.classList.add(cssClass); 6 | div.append(document.createTextNode(args.join(' '))); 7 | container.append(div); 8 | }; 9 | 10 | (async () => { 11 | // Module Worker polyfill from https://stackoverflow.com/a/62963963/6255000. 12 | const supportsWorkerType = () => { 13 | let supports = false; 14 | const tester = { 15 | get type() { 16 | supports = true; 17 | }, 18 | }; 19 | try { 20 | new Worker('data:,""', tester); 21 | } finally { 22 | return supports; 23 | } 24 | }; 25 | if (!supportsWorkerType()) { 26 | await import('./module-workers-polyfill.min.js'); 27 | } 28 | 29 | const worker = new Worker('/demo/worker.js', { 30 | type: 'module', 31 | }); 32 | 33 | worker.addEventListener('message', ({ data }) => { 34 | switch (data.type) { 35 | case 'log': 36 | logHtml(data.payload.cssClass, ...data.payload.args); 37 | break; 38 | default: 39 | logHtml('error', 'Unhandled message:', data.type); 40 | } 41 | }); 42 | })(); 43 | -------------------------------------------------------------------------------- /demo/worker.js: -------------------------------------------------------------------------------- 1 | import sqlite3InitModule from '../index.mjs'; 2 | 3 | const logHtml = function (cssClass, ...args) { 4 | postMessage({ 5 | type: 'log', 6 | payload: { cssClass, args }, 7 | }); 8 | }; 9 | 10 | const log = (...args) => logHtml('', ...args); 11 | const error = (...args) => logHtml('error', ...args); 12 | 13 | const start = function (sqlite3) { 14 | log('Running SQLite3 version', sqlite3.version.libVersion); 15 | let db; 16 | if ('opfs' in sqlite3) { 17 | db = new sqlite3.oo1.OpfsDb('/mydb.sqlite3'); 18 | log('OPFS is available, created persisted database at', db.filename); 19 | } else { 20 | db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct'); 21 | log('OPFS is not available, created transient database', db.filename); 22 | } 23 | try { 24 | log('Creating a table...'); 25 | db.exec('CREATE TABLE IF NOT EXISTS t(a,b)'); 26 | log('Insert some data using exec()...'); 27 | for (let i = 20; i <= 25; ++i) { 28 | db.exec({ 29 | sql: 'INSERT INTO t(a,b) VALUES (?,?)', 30 | bind: [i, i * 2], 31 | }); 32 | } 33 | log('Query data with exec()...'); 34 | db.exec({ 35 | sql: 'SELECT a FROM t ORDER BY a LIMIT 3', 36 | callback: (row) => { 37 | log(row); 38 | }, 39 | }); 40 | } finally { 41 | db.close(); 42 | } 43 | }; 44 | 45 | log('Loading and initializing SQLite3 module...'); 46 | sqlite3InitModule({ 47 | print: log, 48 | printErr: error, 49 | }).then((sqlite3) => { 50 | log('Done initializing. Running demo...'); 51 | try { 52 | start(sqlite3); 53 | } catch (err) { 54 | error(err.name, err.message); 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /demo/wrapped-worker.js: -------------------------------------------------------------------------------- 1 | import { sqlite3Worker1Promiser } from '../index.mjs'; 2 | 3 | (async () => { 4 | const container = document.querySelector('.worker-promiser'); 5 | 6 | const logHtml = function (cssClass, ...args) { 7 | const div = document.createElement('div'); 8 | if (cssClass) div.classList.add(cssClass); 9 | div.append(document.createTextNode(args.join(' '))); 10 | container.append(div); 11 | }; 12 | 13 | try { 14 | logHtml('', 'Loading and initializing SQLite3 module...'); 15 | 16 | const promiser = await new Promise((resolve) => { 17 | const _promiser = sqlite3Worker1Promiser({ 18 | onready: () => { 19 | resolve(_promiser); 20 | }, 21 | }); 22 | }); 23 | 24 | logHtml('', 'Done initializing. Running demo...'); 25 | 26 | let response; 27 | 28 | response = await promiser('config-get', {}); 29 | logHtml('', 'Running SQLite3 version', response.result.version.libVersion); 30 | 31 | response = await promiser('open', { 32 | filename: 'file:worker-promiser.sqlite3?vfs=opfs', 33 | }); 34 | const { dbId } = response; 35 | logHtml( 36 | '', 37 | 'OPFS is available, created persisted database at', 38 | response.result.filename.replace(/^file:(.*?)\?vfs=opfs/, '$1'), 39 | ); 40 | 41 | await promiser('exec', { dbId, sql: 'CREATE TABLE IF NOT EXISTS t(a,b)' }); 42 | logHtml('', 'Creating a table...'); 43 | 44 | logHtml('', 'Insert some data using exec()...'); 45 | for (let i = 20; i <= 25; ++i) { 46 | await promiser('exec', { 47 | dbId, 48 | sql: 'INSERT INTO t(a,b) VALUES (?,?)', 49 | bind: [i, i * 2], 50 | }); 51 | } 52 | 53 | logHtml('', 'Query data with exec()'); 54 | await promiser('exec', { 55 | dbId, 56 | sql: 'SELECT a FROM t ORDER BY a LIMIT 3', 57 | callback: (result) => { 58 | if (!result.row) { 59 | return; 60 | } 61 | logHtml('', result.row); 62 | }, 63 | }); 64 | 65 | await promiser('close', { dbId }); 66 | } catch (err) { 67 | if (!(err instanceof Error)) { 68 | err = new Error(err.result.message); 69 | } 70 | console.error(err.name, err.message); 71 | } 72 | })(); 73 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | import { default as sqlite3InitModule } from './sqlite-wasm/jswasm/sqlite3-bundler-friendly.mjs'; 2 | import './sqlite-wasm/jswasm/sqlite3-worker1-promiser-bundler-friendly.js'; 3 | 4 | const sqlite3Worker1Promiser = globalThis.sqlite3Worker1Promiser; 5 | 6 | export default sqlite3InitModule; 7 | export { sqlite3Worker1Promiser }; 8 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@livestore/sqlite-wasm", 3 | "version": "3.46.0-build1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@livestore/sqlite-wasm", 9 | "version": "3.46.0-build1", 10 | "license": "Apache-2.0", 11 | "bin": { 12 | "sqlite-wasm": "bin/index.js" 13 | }, 14 | "devDependencies": { 15 | "decompress": "^4.2.1", 16 | "http-server": "github:vapier/http-server", 17 | "module-workers-polyfill": "^0.3.2", 18 | "node-fetch": "^3.3.2", 19 | "prettier": "^3.2.4", 20 | "prettier-plugin-jsdoc": "^1.3.0", 21 | "publint": "^0.2.7", 22 | "shx": "^0.3.4" 23 | } 24 | }, 25 | "node_modules/@types/debug": { 26 | "version": "4.1.9", 27 | "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", 28 | "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", 29 | "dev": true, 30 | "dependencies": { 31 | "@types/ms": "*" 32 | } 33 | }, 34 | "node_modules/@types/mdast": { 35 | "version": "4.0.1", 36 | "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.1.tgz", 37 | "integrity": "sha512-IlKct1rUTJ1T81d8OHzyop15kGv9A/ff7Gz7IJgrk6jDb4Udw77pCJ+vq8oxZf4Ghpm+616+i1s/LNg/Vh7d+g==", 38 | "dev": true, 39 | "dependencies": { 40 | "@types/unist": "*" 41 | } 42 | }, 43 | "node_modules/@types/ms": { 44 | "version": "0.7.32", 45 | "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", 46 | "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==", 47 | "dev": true 48 | }, 49 | "node_modules/@types/unist": { 50 | "version": "3.0.0", 51 | "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz", 52 | "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==", 53 | "dev": true 54 | }, 55 | "node_modules/ansi-styles": { 56 | "version": "4.3.0", 57 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 58 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 59 | "dev": true, 60 | "dependencies": { 61 | "color-convert": "^2.0.1" 62 | }, 63 | "engines": { 64 | "node": ">=8" 65 | }, 66 | "funding": { 67 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 68 | } 69 | }, 70 | "node_modules/async": { 71 | "version": "2.6.4", 72 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 73 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 74 | "dev": true, 75 | "dependencies": { 76 | "lodash": "^4.17.14" 77 | } 78 | }, 79 | "node_modules/balanced-match": { 80 | "version": "1.0.2", 81 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 82 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 83 | "dev": true 84 | }, 85 | "node_modules/base64-js": { 86 | "version": "1.5.1", 87 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 88 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 89 | "dev": true, 90 | "funding": [ 91 | { 92 | "type": "github", 93 | "url": "https://github.com/sponsors/feross" 94 | }, 95 | { 96 | "type": "patreon", 97 | "url": "https://www.patreon.com/feross" 98 | }, 99 | { 100 | "type": "consulting", 101 | "url": "https://feross.org/support" 102 | } 103 | ] 104 | }, 105 | "node_modules/basic-auth": { 106 | "version": "2.0.1", 107 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 108 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 109 | "dev": true, 110 | "dependencies": { 111 | "safe-buffer": "5.1.2" 112 | }, 113 | "engines": { 114 | "node": ">= 0.8" 115 | } 116 | }, 117 | "node_modules/binary-searching": { 118 | "version": "2.0.5", 119 | "resolved": "https://registry.npmjs.org/binary-searching/-/binary-searching-2.0.5.tgz", 120 | "integrity": "sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA==", 121 | "dev": true 122 | }, 123 | "node_modules/bl": { 124 | "version": "1.2.3", 125 | "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", 126 | "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", 127 | "dev": true, 128 | "dependencies": { 129 | "readable-stream": "^2.3.5", 130 | "safe-buffer": "^5.1.1" 131 | } 132 | }, 133 | "node_modules/brace-expansion": { 134 | "version": "2.0.1", 135 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 136 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 137 | "dev": true, 138 | "dependencies": { 139 | "balanced-match": "^1.0.0" 140 | } 141 | }, 142 | "node_modules/buffer": { 143 | "version": "5.7.1", 144 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 145 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 146 | "dev": true, 147 | "funding": [ 148 | { 149 | "type": "github", 150 | "url": "https://github.com/sponsors/feross" 151 | }, 152 | { 153 | "type": "patreon", 154 | "url": "https://www.patreon.com/feross" 155 | }, 156 | { 157 | "type": "consulting", 158 | "url": "https://feross.org/support" 159 | } 160 | ], 161 | "dependencies": { 162 | "base64-js": "^1.3.1", 163 | "ieee754": "^1.1.13" 164 | } 165 | }, 166 | "node_modules/buffer-alloc": { 167 | "version": "1.2.0", 168 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 169 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 170 | "dev": true, 171 | "dependencies": { 172 | "buffer-alloc-unsafe": "^1.1.0", 173 | "buffer-fill": "^1.0.0" 174 | } 175 | }, 176 | "node_modules/buffer-alloc-unsafe": { 177 | "version": "1.1.0", 178 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 179 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", 180 | "dev": true 181 | }, 182 | "node_modules/buffer-crc32": { 183 | "version": "0.2.13", 184 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 185 | "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", 186 | "dev": true, 187 | "engines": { 188 | "node": "*" 189 | } 190 | }, 191 | "node_modules/buffer-fill": { 192 | "version": "1.0.0", 193 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 194 | "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", 195 | "dev": true 196 | }, 197 | "node_modules/call-bind": { 198 | "version": "1.0.2", 199 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 200 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 201 | "dev": true, 202 | "dependencies": { 203 | "function-bind": "^1.1.1", 204 | "get-intrinsic": "^1.0.2" 205 | }, 206 | "funding": { 207 | "url": "https://github.com/sponsors/ljharb" 208 | } 209 | }, 210 | "node_modules/chalk": { 211 | "version": "4.1.2", 212 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 213 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 214 | "dev": true, 215 | "dependencies": { 216 | "ansi-styles": "^4.1.0", 217 | "supports-color": "^7.1.0" 218 | }, 219 | "engines": { 220 | "node": ">=10" 221 | }, 222 | "funding": { 223 | "url": "https://github.com/chalk/chalk?sponsor=1" 224 | } 225 | }, 226 | "node_modules/character-entities": { 227 | "version": "2.0.2", 228 | "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", 229 | "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", 230 | "dev": true, 231 | "funding": { 232 | "type": "github", 233 | "url": "https://github.com/sponsors/wooorm" 234 | } 235 | }, 236 | "node_modules/color-convert": { 237 | "version": "2.0.1", 238 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 239 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 240 | "dev": true, 241 | "dependencies": { 242 | "color-name": "~1.1.4" 243 | }, 244 | "engines": { 245 | "node": ">=7.0.0" 246 | } 247 | }, 248 | "node_modules/color-name": { 249 | "version": "1.1.4", 250 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 251 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 252 | "dev": true 253 | }, 254 | "node_modules/commander": { 255 | "version": "2.20.3", 256 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 257 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 258 | "dev": true 259 | }, 260 | "node_modules/comment-parser": { 261 | "version": "1.4.0", 262 | "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", 263 | "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", 264 | "dev": true, 265 | "engines": { 266 | "node": ">= 12.0.0" 267 | } 268 | }, 269 | "node_modules/concat-map": { 270 | "version": "0.0.1", 271 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 272 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 273 | "dev": true 274 | }, 275 | "node_modules/core-util-is": { 276 | "version": "1.0.2", 277 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 278 | "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", 279 | "dev": true 280 | }, 281 | "node_modules/corser": { 282 | "version": "2.0.1", 283 | "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", 284 | "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", 285 | "dev": true, 286 | "engines": { 287 | "node": ">= 0.4.0" 288 | } 289 | }, 290 | "node_modules/data-uri-to-buffer": { 291 | "version": "4.0.1", 292 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", 293 | "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", 294 | "dev": true, 295 | "engines": { 296 | "node": ">= 12" 297 | } 298 | }, 299 | "node_modules/debug": { 300 | "version": "3.2.7", 301 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 302 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 303 | "dev": true, 304 | "dependencies": { 305 | "ms": "^2.1.1" 306 | } 307 | }, 308 | "node_modules/decode-named-character-reference": { 309 | "version": "1.0.2", 310 | "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", 311 | "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", 312 | "dev": true, 313 | "dependencies": { 314 | "character-entities": "^2.0.0" 315 | }, 316 | "funding": { 317 | "type": "github", 318 | "url": "https://github.com/sponsors/wooorm" 319 | } 320 | }, 321 | "node_modules/decompress": { 322 | "version": "4.2.1", 323 | "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", 324 | "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", 325 | "dev": true, 326 | "dependencies": { 327 | "decompress-tar": "^4.0.0", 328 | "decompress-tarbz2": "^4.0.0", 329 | "decompress-targz": "^4.0.0", 330 | "decompress-unzip": "^4.0.1", 331 | "graceful-fs": "^4.1.10", 332 | "make-dir": "^1.0.0", 333 | "pify": "^2.3.0", 334 | "strip-dirs": "^2.0.0" 335 | }, 336 | "engines": { 337 | "node": ">=4" 338 | } 339 | }, 340 | "node_modules/decompress-tar": { 341 | "version": "4.1.1", 342 | "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", 343 | "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", 344 | "dev": true, 345 | "dependencies": { 346 | "file-type": "^5.2.0", 347 | "is-stream": "^1.1.0", 348 | "tar-stream": "^1.5.2" 349 | }, 350 | "engines": { 351 | "node": ">=4" 352 | } 353 | }, 354 | "node_modules/decompress-tarbz2": { 355 | "version": "4.1.1", 356 | "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", 357 | "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", 358 | "dev": true, 359 | "dependencies": { 360 | "decompress-tar": "^4.1.0", 361 | "file-type": "^6.1.0", 362 | "is-stream": "^1.1.0", 363 | "seek-bzip": "^1.0.5", 364 | "unbzip2-stream": "^1.0.9" 365 | }, 366 | "engines": { 367 | "node": ">=4" 368 | } 369 | }, 370 | "node_modules/decompress-tarbz2/node_modules/file-type": { 371 | "version": "6.2.0", 372 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", 373 | "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", 374 | "dev": true, 375 | "engines": { 376 | "node": ">=4" 377 | } 378 | }, 379 | "node_modules/decompress-targz": { 380 | "version": "4.1.1", 381 | "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", 382 | "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", 383 | "dev": true, 384 | "dependencies": { 385 | "decompress-tar": "^4.1.1", 386 | "file-type": "^5.2.0", 387 | "is-stream": "^1.1.0" 388 | }, 389 | "engines": { 390 | "node": ">=4" 391 | } 392 | }, 393 | "node_modules/decompress-unzip": { 394 | "version": "4.0.1", 395 | "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", 396 | "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", 397 | "dev": true, 398 | "dependencies": { 399 | "file-type": "^3.8.0", 400 | "get-stream": "^2.2.0", 401 | "pify": "^2.3.0", 402 | "yauzl": "^2.4.2" 403 | }, 404 | "engines": { 405 | "node": ">=4" 406 | } 407 | }, 408 | "node_modules/decompress-unzip/node_modules/file-type": { 409 | "version": "3.9.0", 410 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", 411 | "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", 412 | "dev": true, 413 | "engines": { 414 | "node": ">=0.10.0" 415 | } 416 | }, 417 | "node_modules/dequal": { 418 | "version": "2.0.3", 419 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 420 | "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 421 | "dev": true, 422 | "engines": { 423 | "node": ">=6" 424 | } 425 | }, 426 | "node_modules/devlop": { 427 | "version": "1.1.0", 428 | "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", 429 | "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", 430 | "dev": true, 431 | "dependencies": { 432 | "dequal": "^2.0.0" 433 | }, 434 | "funding": { 435 | "type": "github", 436 | "url": "https://github.com/sponsors/wooorm" 437 | } 438 | }, 439 | "node_modules/end-of-stream": { 440 | "version": "1.4.4", 441 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 442 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 443 | "dev": true, 444 | "dependencies": { 445 | "once": "^1.4.0" 446 | } 447 | }, 448 | "node_modules/eventemitter3": { 449 | "version": "4.0.7", 450 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 451 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", 452 | "dev": true 453 | }, 454 | "node_modules/fd-slicer": { 455 | "version": "1.1.0", 456 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 457 | "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", 458 | "dev": true, 459 | "dependencies": { 460 | "pend": "~1.2.0" 461 | } 462 | }, 463 | "node_modules/fetch-blob": { 464 | "version": "3.2.0", 465 | "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", 466 | "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", 467 | "dev": true, 468 | "funding": [ 469 | { 470 | "type": "github", 471 | "url": "https://github.com/sponsors/jimmywarting" 472 | }, 473 | { 474 | "type": "paypal", 475 | "url": "https://paypal.me/jimmywarting" 476 | } 477 | ], 478 | "dependencies": { 479 | "node-domexception": "^1.0.0", 480 | "web-streams-polyfill": "^3.0.3" 481 | }, 482 | "engines": { 483 | "node": "^12.20 || >= 14.13" 484 | } 485 | }, 486 | "node_modules/file-type": { 487 | "version": "5.2.0", 488 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", 489 | "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", 490 | "dev": true, 491 | "engines": { 492 | "node": ">=4" 493 | } 494 | }, 495 | "node_modules/follow-redirects": { 496 | "version": "1.15.5", 497 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", 498 | "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", 499 | "dev": true, 500 | "funding": [ 501 | { 502 | "type": "individual", 503 | "url": "https://github.com/sponsors/RubenVerborgh" 504 | } 505 | ], 506 | "engines": { 507 | "node": ">=4.0" 508 | }, 509 | "peerDependenciesMeta": { 510 | "debug": { 511 | "optional": true 512 | } 513 | } 514 | }, 515 | "node_modules/formdata-polyfill": { 516 | "version": "4.0.10", 517 | "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", 518 | "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", 519 | "dev": true, 520 | "dependencies": { 521 | "fetch-blob": "^3.1.2" 522 | }, 523 | "engines": { 524 | "node": ">=12.20.0" 525 | } 526 | }, 527 | "node_modules/fs-constants": { 528 | "version": "1.0.0", 529 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 530 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 531 | "dev": true 532 | }, 533 | "node_modules/fs.realpath": { 534 | "version": "1.0.0", 535 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 536 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 537 | "dev": true 538 | }, 539 | "node_modules/function-bind": { 540 | "version": "1.1.1", 541 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 542 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 543 | "dev": true 544 | }, 545 | "node_modules/get-intrinsic": { 546 | "version": "1.2.0", 547 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", 548 | "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", 549 | "dev": true, 550 | "dependencies": { 551 | "function-bind": "^1.1.1", 552 | "has": "^1.0.3", 553 | "has-symbols": "^1.0.3" 554 | }, 555 | "funding": { 556 | "url": "https://github.com/sponsors/ljharb" 557 | } 558 | }, 559 | "node_modules/get-stream": { 560 | "version": "2.3.1", 561 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", 562 | "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", 563 | "dev": true, 564 | "dependencies": { 565 | "object-assign": "^4.0.1", 566 | "pinkie-promise": "^2.0.0" 567 | }, 568 | "engines": { 569 | "node": ">=0.10.0" 570 | } 571 | }, 572 | "node_modules/glob": { 573 | "version": "8.1.0", 574 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", 575 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", 576 | "dev": true, 577 | "dependencies": { 578 | "fs.realpath": "^1.0.0", 579 | "inflight": "^1.0.4", 580 | "inherits": "2", 581 | "minimatch": "^5.0.1", 582 | "once": "^1.3.0" 583 | }, 584 | "engines": { 585 | "node": ">=12" 586 | }, 587 | "funding": { 588 | "url": "https://github.com/sponsors/isaacs" 589 | } 590 | }, 591 | "node_modules/graceful-fs": { 592 | "version": "4.2.11", 593 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 594 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 595 | "dev": true 596 | }, 597 | "node_modules/has": { 598 | "version": "1.0.3", 599 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 600 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 601 | "dev": true, 602 | "dependencies": { 603 | "function-bind": "^1.1.1" 604 | }, 605 | "engines": { 606 | "node": ">= 0.4.0" 607 | } 608 | }, 609 | "node_modules/has-flag": { 610 | "version": "4.0.0", 611 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 612 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 613 | "dev": true, 614 | "engines": { 615 | "node": ">=8" 616 | } 617 | }, 618 | "node_modules/has-symbols": { 619 | "version": "1.0.3", 620 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 621 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 622 | "dev": true, 623 | "engines": { 624 | "node": ">= 0.4" 625 | }, 626 | "funding": { 627 | "url": "https://github.com/sponsors/ljharb" 628 | } 629 | }, 630 | "node_modules/he": { 631 | "version": "1.2.0", 632 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 633 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 634 | "dev": true, 635 | "bin": { 636 | "he": "bin/he" 637 | } 638 | }, 639 | "node_modules/html-encoding-sniffer": { 640 | "version": "3.0.0", 641 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", 642 | "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", 643 | "dev": true, 644 | "dependencies": { 645 | "whatwg-encoding": "^2.0.0" 646 | }, 647 | "engines": { 648 | "node": ">=12" 649 | } 650 | }, 651 | "node_modules/http-proxy": { 652 | "version": "1.18.1", 653 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", 654 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", 655 | "dev": true, 656 | "dependencies": { 657 | "eventemitter3": "^4.0.0", 658 | "follow-redirects": "^1.0.0", 659 | "requires-port": "^1.0.0" 660 | }, 661 | "engines": { 662 | "node": ">=8.0.0" 663 | } 664 | }, 665 | "node_modules/http-server": { 666 | "version": "14.1.0", 667 | "resolved": "git+ssh://git@github.com/vapier/http-server.git#35fad5cd29005748916d1bca24db83ab6976ba41", 668 | "dev": true, 669 | "license": "MIT", 670 | "dependencies": { 671 | "basic-auth": "^2.0.1", 672 | "chalk": "^4.1.2", 673 | "corser": "^2.0.1", 674 | "he": "^1.2.0", 675 | "html-encoding-sniffer": "^3.0.0", 676 | "http-proxy": "^1.18.1", 677 | "mime": "^1.6.0", 678 | "minimist": "^1.2.6", 679 | "opener": "^1.5.1", 680 | "portfinder": "^1.0.28", 681 | "secure-compare": "3.0.1", 682 | "union": "~0.5.0", 683 | "url-join": "^4.0.1" 684 | }, 685 | "bin": { 686 | "http-server": "bin/http-server" 687 | }, 688 | "engines": { 689 | "node": ">=12" 690 | } 691 | }, 692 | "node_modules/iconv-lite": { 693 | "version": "0.6.3", 694 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 695 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 696 | "dev": true, 697 | "dependencies": { 698 | "safer-buffer": ">= 2.1.2 < 3.0.0" 699 | }, 700 | "engines": { 701 | "node": ">=0.10.0" 702 | } 703 | }, 704 | "node_modules/ieee754": { 705 | "version": "1.2.1", 706 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 707 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 708 | "dev": true, 709 | "funding": [ 710 | { 711 | "type": "github", 712 | "url": "https://github.com/sponsors/feross" 713 | }, 714 | { 715 | "type": "patreon", 716 | "url": "https://www.patreon.com/feross" 717 | }, 718 | { 719 | "type": "consulting", 720 | "url": "https://feross.org/support" 721 | } 722 | ] 723 | }, 724 | "node_modules/ignore-walk": { 725 | "version": "5.0.1", 726 | "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", 727 | "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", 728 | "dev": true, 729 | "dependencies": { 730 | "minimatch": "^5.0.1" 731 | }, 732 | "engines": { 733 | "node": "^12.13.0 || ^14.15.0 || >=16.0.0" 734 | } 735 | }, 736 | "node_modules/inflight": { 737 | "version": "1.0.6", 738 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 739 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 740 | "dev": true, 741 | "dependencies": { 742 | "once": "^1.3.0", 743 | "wrappy": "1" 744 | } 745 | }, 746 | "node_modules/inherits": { 747 | "version": "2.0.4", 748 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 749 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 750 | "dev": true 751 | }, 752 | "node_modules/interpret": { 753 | "version": "1.4.0", 754 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", 755 | "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", 756 | "dev": true, 757 | "engines": { 758 | "node": ">= 0.10" 759 | } 760 | }, 761 | "node_modules/is-core-module": { 762 | "version": "2.12.1", 763 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", 764 | "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", 765 | "dev": true, 766 | "dependencies": { 767 | "has": "^1.0.3" 768 | }, 769 | "funding": { 770 | "url": "https://github.com/sponsors/ljharb" 771 | } 772 | }, 773 | "node_modules/is-natural-number": { 774 | "version": "4.0.1", 775 | "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", 776 | "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", 777 | "dev": true 778 | }, 779 | "node_modules/is-stream": { 780 | "version": "1.1.0", 781 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 782 | "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", 783 | "dev": true, 784 | "engines": { 785 | "node": ">=0.10.0" 786 | } 787 | }, 788 | "node_modules/isarray": { 789 | "version": "1.0.0", 790 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 791 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", 792 | "dev": true 793 | }, 794 | "node_modules/lodash": { 795 | "version": "4.17.21", 796 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 797 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 798 | "dev": true 799 | }, 800 | "node_modules/make-dir": { 801 | "version": "1.3.0", 802 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", 803 | "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", 804 | "dev": true, 805 | "dependencies": { 806 | "pify": "^3.0.0" 807 | }, 808 | "engines": { 809 | "node": ">=4" 810 | } 811 | }, 812 | "node_modules/make-dir/node_modules/pify": { 813 | "version": "3.0.0", 814 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 815 | "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", 816 | "dev": true, 817 | "engines": { 818 | "node": ">=4" 819 | } 820 | }, 821 | "node_modules/mdast-util-from-markdown": { 822 | "version": "2.0.0", 823 | "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", 824 | "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", 825 | "dev": true, 826 | "dependencies": { 827 | "@types/mdast": "^4.0.0", 828 | "@types/unist": "^3.0.0", 829 | "decode-named-character-reference": "^1.0.0", 830 | "devlop": "^1.0.0", 831 | "mdast-util-to-string": "^4.0.0", 832 | "micromark": "^4.0.0", 833 | "micromark-util-decode-numeric-character-reference": "^2.0.0", 834 | "micromark-util-decode-string": "^2.0.0", 835 | "micromark-util-normalize-identifier": "^2.0.0", 836 | "micromark-util-symbol": "^2.0.0", 837 | "micromark-util-types": "^2.0.0", 838 | "unist-util-stringify-position": "^4.0.0" 839 | }, 840 | "funding": { 841 | "type": "opencollective", 842 | "url": "https://opencollective.com/unified" 843 | } 844 | }, 845 | "node_modules/mdast-util-to-string": { 846 | "version": "4.0.0", 847 | "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", 848 | "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", 849 | "dev": true, 850 | "dependencies": { 851 | "@types/mdast": "^4.0.0" 852 | }, 853 | "funding": { 854 | "type": "opencollective", 855 | "url": "https://opencollective.com/unified" 856 | } 857 | }, 858 | "node_modules/micromark": { 859 | "version": "4.0.0", 860 | "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", 861 | "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", 862 | "dev": true, 863 | "funding": [ 864 | { 865 | "type": "GitHub Sponsors", 866 | "url": "https://github.com/sponsors/unifiedjs" 867 | }, 868 | { 869 | "type": "OpenCollective", 870 | "url": "https://opencollective.com/unified" 871 | } 872 | ], 873 | "dependencies": { 874 | "@types/debug": "^4.0.0", 875 | "debug": "^4.0.0", 876 | "decode-named-character-reference": "^1.0.0", 877 | "devlop": "^1.0.0", 878 | "micromark-core-commonmark": "^2.0.0", 879 | "micromark-factory-space": "^2.0.0", 880 | "micromark-util-character": "^2.0.0", 881 | "micromark-util-chunked": "^2.0.0", 882 | "micromark-util-combine-extensions": "^2.0.0", 883 | "micromark-util-decode-numeric-character-reference": "^2.0.0", 884 | "micromark-util-encode": "^2.0.0", 885 | "micromark-util-normalize-identifier": "^2.0.0", 886 | "micromark-util-resolve-all": "^2.0.0", 887 | "micromark-util-sanitize-uri": "^2.0.0", 888 | "micromark-util-subtokenize": "^2.0.0", 889 | "micromark-util-symbol": "^2.0.0", 890 | "micromark-util-types": "^2.0.0" 891 | } 892 | }, 893 | "node_modules/micromark-core-commonmark": { 894 | "version": "2.0.0", 895 | "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", 896 | "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", 897 | "dev": true, 898 | "funding": [ 899 | { 900 | "type": "GitHub Sponsors", 901 | "url": "https://github.com/sponsors/unifiedjs" 902 | }, 903 | { 904 | "type": "OpenCollective", 905 | "url": "https://opencollective.com/unified" 906 | } 907 | ], 908 | "dependencies": { 909 | "decode-named-character-reference": "^1.0.0", 910 | "devlop": "^1.0.0", 911 | "micromark-factory-destination": "^2.0.0", 912 | "micromark-factory-label": "^2.0.0", 913 | "micromark-factory-space": "^2.0.0", 914 | "micromark-factory-title": "^2.0.0", 915 | "micromark-factory-whitespace": "^2.0.0", 916 | "micromark-util-character": "^2.0.0", 917 | "micromark-util-chunked": "^2.0.0", 918 | "micromark-util-classify-character": "^2.0.0", 919 | "micromark-util-html-tag-name": "^2.0.0", 920 | "micromark-util-normalize-identifier": "^2.0.0", 921 | "micromark-util-resolve-all": "^2.0.0", 922 | "micromark-util-subtokenize": "^2.0.0", 923 | "micromark-util-symbol": "^2.0.0", 924 | "micromark-util-types": "^2.0.0" 925 | } 926 | }, 927 | "node_modules/micromark-factory-destination": { 928 | "version": "2.0.0", 929 | "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", 930 | "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", 931 | "dev": true, 932 | "funding": [ 933 | { 934 | "type": "GitHub Sponsors", 935 | "url": "https://github.com/sponsors/unifiedjs" 936 | }, 937 | { 938 | "type": "OpenCollective", 939 | "url": "https://opencollective.com/unified" 940 | } 941 | ], 942 | "dependencies": { 943 | "micromark-util-character": "^2.0.0", 944 | "micromark-util-symbol": "^2.0.0", 945 | "micromark-util-types": "^2.0.0" 946 | } 947 | }, 948 | "node_modules/micromark-factory-label": { 949 | "version": "2.0.0", 950 | "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", 951 | "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", 952 | "dev": true, 953 | "funding": [ 954 | { 955 | "type": "GitHub Sponsors", 956 | "url": "https://github.com/sponsors/unifiedjs" 957 | }, 958 | { 959 | "type": "OpenCollective", 960 | "url": "https://opencollective.com/unified" 961 | } 962 | ], 963 | "dependencies": { 964 | "devlop": "^1.0.0", 965 | "micromark-util-character": "^2.0.0", 966 | "micromark-util-symbol": "^2.0.0", 967 | "micromark-util-types": "^2.0.0" 968 | } 969 | }, 970 | "node_modules/micromark-factory-space": { 971 | "version": "2.0.0", 972 | "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", 973 | "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", 974 | "dev": true, 975 | "funding": [ 976 | { 977 | "type": "GitHub Sponsors", 978 | "url": "https://github.com/sponsors/unifiedjs" 979 | }, 980 | { 981 | "type": "OpenCollective", 982 | "url": "https://opencollective.com/unified" 983 | } 984 | ], 985 | "dependencies": { 986 | "micromark-util-character": "^2.0.0", 987 | "micromark-util-types": "^2.0.0" 988 | } 989 | }, 990 | "node_modules/micromark-factory-title": { 991 | "version": "2.0.0", 992 | "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", 993 | "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", 994 | "dev": true, 995 | "funding": [ 996 | { 997 | "type": "GitHub Sponsors", 998 | "url": "https://github.com/sponsors/unifiedjs" 999 | }, 1000 | { 1001 | "type": "OpenCollective", 1002 | "url": "https://opencollective.com/unified" 1003 | } 1004 | ], 1005 | "dependencies": { 1006 | "micromark-factory-space": "^2.0.0", 1007 | "micromark-util-character": "^2.0.0", 1008 | "micromark-util-symbol": "^2.0.0", 1009 | "micromark-util-types": "^2.0.0" 1010 | } 1011 | }, 1012 | "node_modules/micromark-factory-whitespace": { 1013 | "version": "2.0.0", 1014 | "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", 1015 | "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", 1016 | "dev": true, 1017 | "funding": [ 1018 | { 1019 | "type": "GitHub Sponsors", 1020 | "url": "https://github.com/sponsors/unifiedjs" 1021 | }, 1022 | { 1023 | "type": "OpenCollective", 1024 | "url": "https://opencollective.com/unified" 1025 | } 1026 | ], 1027 | "dependencies": { 1028 | "micromark-factory-space": "^2.0.0", 1029 | "micromark-util-character": "^2.0.0", 1030 | "micromark-util-symbol": "^2.0.0", 1031 | "micromark-util-types": "^2.0.0" 1032 | } 1033 | }, 1034 | "node_modules/micromark-util-character": { 1035 | "version": "2.0.1", 1036 | "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", 1037 | "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", 1038 | "dev": true, 1039 | "funding": [ 1040 | { 1041 | "type": "GitHub Sponsors", 1042 | "url": "https://github.com/sponsors/unifiedjs" 1043 | }, 1044 | { 1045 | "type": "OpenCollective", 1046 | "url": "https://opencollective.com/unified" 1047 | } 1048 | ], 1049 | "dependencies": { 1050 | "micromark-util-symbol": "^2.0.0", 1051 | "micromark-util-types": "^2.0.0" 1052 | } 1053 | }, 1054 | "node_modules/micromark-util-chunked": { 1055 | "version": "2.0.0", 1056 | "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", 1057 | "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", 1058 | "dev": true, 1059 | "funding": [ 1060 | { 1061 | "type": "GitHub Sponsors", 1062 | "url": "https://github.com/sponsors/unifiedjs" 1063 | }, 1064 | { 1065 | "type": "OpenCollective", 1066 | "url": "https://opencollective.com/unified" 1067 | } 1068 | ], 1069 | "dependencies": { 1070 | "micromark-util-symbol": "^2.0.0" 1071 | } 1072 | }, 1073 | "node_modules/micromark-util-classify-character": { 1074 | "version": "2.0.0", 1075 | "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", 1076 | "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", 1077 | "dev": true, 1078 | "funding": [ 1079 | { 1080 | "type": "GitHub Sponsors", 1081 | "url": "https://github.com/sponsors/unifiedjs" 1082 | }, 1083 | { 1084 | "type": "OpenCollective", 1085 | "url": "https://opencollective.com/unified" 1086 | } 1087 | ], 1088 | "dependencies": { 1089 | "micromark-util-character": "^2.0.0", 1090 | "micromark-util-symbol": "^2.0.0", 1091 | "micromark-util-types": "^2.0.0" 1092 | } 1093 | }, 1094 | "node_modules/micromark-util-combine-extensions": { 1095 | "version": "2.0.0", 1096 | "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", 1097 | "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", 1098 | "dev": true, 1099 | "funding": [ 1100 | { 1101 | "type": "GitHub Sponsors", 1102 | "url": "https://github.com/sponsors/unifiedjs" 1103 | }, 1104 | { 1105 | "type": "OpenCollective", 1106 | "url": "https://opencollective.com/unified" 1107 | } 1108 | ], 1109 | "dependencies": { 1110 | "micromark-util-chunked": "^2.0.0", 1111 | "micromark-util-types": "^2.0.0" 1112 | } 1113 | }, 1114 | "node_modules/micromark-util-decode-numeric-character-reference": { 1115 | "version": "2.0.0", 1116 | "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.0.tgz", 1117 | "integrity": "sha512-pIgcsGxpHEtTG/rPJRz/HOLSqp5VTuIIjXlPI+6JSDlK2oljApusG6KzpS8AF0ENUMCHlC/IBb5B9xdFiVlm5Q==", 1118 | "dev": true, 1119 | "funding": [ 1120 | { 1121 | "type": "GitHub Sponsors", 1122 | "url": "https://github.com/sponsors/unifiedjs" 1123 | }, 1124 | { 1125 | "type": "OpenCollective", 1126 | "url": "https://opencollective.com/unified" 1127 | } 1128 | ], 1129 | "dependencies": { 1130 | "micromark-util-symbol": "^2.0.0" 1131 | } 1132 | }, 1133 | "node_modules/micromark-util-decode-string": { 1134 | "version": "2.0.0", 1135 | "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", 1136 | "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", 1137 | "dev": true, 1138 | "funding": [ 1139 | { 1140 | "type": "GitHub Sponsors", 1141 | "url": "https://github.com/sponsors/unifiedjs" 1142 | }, 1143 | { 1144 | "type": "OpenCollective", 1145 | "url": "https://opencollective.com/unified" 1146 | } 1147 | ], 1148 | "dependencies": { 1149 | "decode-named-character-reference": "^1.0.0", 1150 | "micromark-util-character": "^2.0.0", 1151 | "micromark-util-decode-numeric-character-reference": "^2.0.0", 1152 | "micromark-util-symbol": "^2.0.0" 1153 | } 1154 | }, 1155 | "node_modules/micromark-util-encode": { 1156 | "version": "2.0.0", 1157 | "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", 1158 | "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", 1159 | "dev": true, 1160 | "funding": [ 1161 | { 1162 | "type": "GitHub Sponsors", 1163 | "url": "https://github.com/sponsors/unifiedjs" 1164 | }, 1165 | { 1166 | "type": "OpenCollective", 1167 | "url": "https://opencollective.com/unified" 1168 | } 1169 | ] 1170 | }, 1171 | "node_modules/micromark-util-html-tag-name": { 1172 | "version": "2.0.0", 1173 | "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", 1174 | "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", 1175 | "dev": true, 1176 | "funding": [ 1177 | { 1178 | "type": "GitHub Sponsors", 1179 | "url": "https://github.com/sponsors/unifiedjs" 1180 | }, 1181 | { 1182 | "type": "OpenCollective", 1183 | "url": "https://opencollective.com/unified" 1184 | } 1185 | ] 1186 | }, 1187 | "node_modules/micromark-util-normalize-identifier": { 1188 | "version": "2.0.0", 1189 | "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", 1190 | "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", 1191 | "dev": true, 1192 | "funding": [ 1193 | { 1194 | "type": "GitHub Sponsors", 1195 | "url": "https://github.com/sponsors/unifiedjs" 1196 | }, 1197 | { 1198 | "type": "OpenCollective", 1199 | "url": "https://opencollective.com/unified" 1200 | } 1201 | ], 1202 | "dependencies": { 1203 | "micromark-util-symbol": "^2.0.0" 1204 | } 1205 | }, 1206 | "node_modules/micromark-util-resolve-all": { 1207 | "version": "2.0.0", 1208 | "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", 1209 | "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", 1210 | "dev": true, 1211 | "funding": [ 1212 | { 1213 | "type": "GitHub Sponsors", 1214 | "url": "https://github.com/sponsors/unifiedjs" 1215 | }, 1216 | { 1217 | "type": "OpenCollective", 1218 | "url": "https://opencollective.com/unified" 1219 | } 1220 | ], 1221 | "dependencies": { 1222 | "micromark-util-types": "^2.0.0" 1223 | } 1224 | }, 1225 | "node_modules/micromark-util-sanitize-uri": { 1226 | "version": "2.0.0", 1227 | "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", 1228 | "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", 1229 | "dev": true, 1230 | "funding": [ 1231 | { 1232 | "type": "GitHub Sponsors", 1233 | "url": "https://github.com/sponsors/unifiedjs" 1234 | }, 1235 | { 1236 | "type": "OpenCollective", 1237 | "url": "https://opencollective.com/unified" 1238 | } 1239 | ], 1240 | "dependencies": { 1241 | "micromark-util-character": "^2.0.0", 1242 | "micromark-util-encode": "^2.0.0", 1243 | "micromark-util-symbol": "^2.0.0" 1244 | } 1245 | }, 1246 | "node_modules/micromark-util-subtokenize": { 1247 | "version": "2.0.0", 1248 | "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", 1249 | "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", 1250 | "dev": true, 1251 | "funding": [ 1252 | { 1253 | "type": "GitHub Sponsors", 1254 | "url": "https://github.com/sponsors/unifiedjs" 1255 | }, 1256 | { 1257 | "type": "OpenCollective", 1258 | "url": "https://opencollective.com/unified" 1259 | } 1260 | ], 1261 | "dependencies": { 1262 | "devlop": "^1.0.0", 1263 | "micromark-util-chunked": "^2.0.0", 1264 | "micromark-util-symbol": "^2.0.0", 1265 | "micromark-util-types": "^2.0.0" 1266 | } 1267 | }, 1268 | "node_modules/micromark-util-symbol": { 1269 | "version": "2.0.0", 1270 | "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", 1271 | "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", 1272 | "dev": true, 1273 | "funding": [ 1274 | { 1275 | "type": "GitHub Sponsors", 1276 | "url": "https://github.com/sponsors/unifiedjs" 1277 | }, 1278 | { 1279 | "type": "OpenCollective", 1280 | "url": "https://opencollective.com/unified" 1281 | } 1282 | ] 1283 | }, 1284 | "node_modules/micromark-util-types": { 1285 | "version": "2.0.0", 1286 | "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", 1287 | "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", 1288 | "dev": true, 1289 | "funding": [ 1290 | { 1291 | "type": "GitHub Sponsors", 1292 | "url": "https://github.com/sponsors/unifiedjs" 1293 | }, 1294 | { 1295 | "type": "OpenCollective", 1296 | "url": "https://opencollective.com/unified" 1297 | } 1298 | ] 1299 | }, 1300 | "node_modules/micromark/node_modules/debug": { 1301 | "version": "4.3.4", 1302 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1303 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1304 | "dev": true, 1305 | "dependencies": { 1306 | "ms": "2.1.2" 1307 | }, 1308 | "engines": { 1309 | "node": ">=6.0" 1310 | }, 1311 | "peerDependenciesMeta": { 1312 | "supports-color": { 1313 | "optional": true 1314 | } 1315 | } 1316 | }, 1317 | "node_modules/micromark/node_modules/ms": { 1318 | "version": "2.1.2", 1319 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1320 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1321 | "dev": true 1322 | }, 1323 | "node_modules/mime": { 1324 | "version": "1.6.0", 1325 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1326 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1327 | "dev": true, 1328 | "bin": { 1329 | "mime": "cli.js" 1330 | }, 1331 | "engines": { 1332 | "node": ">=4" 1333 | } 1334 | }, 1335 | "node_modules/minimatch": { 1336 | "version": "5.1.6", 1337 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 1338 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 1339 | "dev": true, 1340 | "dependencies": { 1341 | "brace-expansion": "^2.0.1" 1342 | }, 1343 | "engines": { 1344 | "node": ">=10" 1345 | } 1346 | }, 1347 | "node_modules/minimist": { 1348 | "version": "1.2.8", 1349 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 1350 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 1351 | "dev": true, 1352 | "funding": { 1353 | "url": "https://github.com/sponsors/ljharb" 1354 | } 1355 | }, 1356 | "node_modules/mkdirp": { 1357 | "version": "0.5.6", 1358 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 1359 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 1360 | "dev": true, 1361 | "dependencies": { 1362 | "minimist": "^1.2.6" 1363 | }, 1364 | "bin": { 1365 | "mkdirp": "bin/cmd.js" 1366 | } 1367 | }, 1368 | "node_modules/module-workers-polyfill": { 1369 | "version": "0.3.2", 1370 | "resolved": "https://registry.npmjs.org/module-workers-polyfill/-/module-workers-polyfill-0.3.2.tgz", 1371 | "integrity": "sha512-zcEMj8vUGYu1Tc5s2GK84MBzRSweec/Ef6F7phRF8QlTfTf2upFNsR3W0lDhvN2E6o1TTXbFIYynD0rQSlynPw==", 1372 | "dev": true 1373 | }, 1374 | "node_modules/mri": { 1375 | "version": "1.2.0", 1376 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 1377 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", 1378 | "dev": true, 1379 | "engines": { 1380 | "node": ">=4" 1381 | } 1382 | }, 1383 | "node_modules/ms": { 1384 | "version": "2.1.3", 1385 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1386 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1387 | "dev": true 1388 | }, 1389 | "node_modules/node-domexception": { 1390 | "version": "1.0.0", 1391 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 1392 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 1393 | "dev": true, 1394 | "funding": [ 1395 | { 1396 | "type": "github", 1397 | "url": "https://github.com/sponsors/jimmywarting" 1398 | }, 1399 | { 1400 | "type": "github", 1401 | "url": "https://paypal.me/jimmywarting" 1402 | } 1403 | ], 1404 | "engines": { 1405 | "node": ">=10.5.0" 1406 | } 1407 | }, 1408 | "node_modules/node-fetch": { 1409 | "version": "3.3.2", 1410 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", 1411 | "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", 1412 | "dev": true, 1413 | "dependencies": { 1414 | "data-uri-to-buffer": "^4.0.0", 1415 | "fetch-blob": "^3.1.4", 1416 | "formdata-polyfill": "^4.0.10" 1417 | }, 1418 | "engines": { 1419 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1420 | }, 1421 | "funding": { 1422 | "type": "opencollective", 1423 | "url": "https://opencollective.com/node-fetch" 1424 | } 1425 | }, 1426 | "node_modules/npm-bundled": { 1427 | "version": "2.0.1", 1428 | "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", 1429 | "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", 1430 | "dev": true, 1431 | "dependencies": { 1432 | "npm-normalize-package-bin": "^2.0.0" 1433 | }, 1434 | "engines": { 1435 | "node": "^12.13.0 || ^14.15.0 || >=16.0.0" 1436 | } 1437 | }, 1438 | "node_modules/npm-normalize-package-bin": { 1439 | "version": "2.0.0", 1440 | "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", 1441 | "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", 1442 | "dev": true, 1443 | "engines": { 1444 | "node": "^12.13.0 || ^14.15.0 || >=16.0.0" 1445 | } 1446 | }, 1447 | "node_modules/npm-packlist": { 1448 | "version": "5.1.3", 1449 | "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", 1450 | "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", 1451 | "dev": true, 1452 | "dependencies": { 1453 | "glob": "^8.0.1", 1454 | "ignore-walk": "^5.0.1", 1455 | "npm-bundled": "^2.0.0", 1456 | "npm-normalize-package-bin": "^2.0.0" 1457 | }, 1458 | "bin": { 1459 | "npm-packlist": "bin/index.js" 1460 | }, 1461 | "engines": { 1462 | "node": "^12.13.0 || ^14.15.0 || >=16.0.0" 1463 | } 1464 | }, 1465 | "node_modules/object-assign": { 1466 | "version": "4.1.1", 1467 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1468 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1469 | "dev": true, 1470 | "engines": { 1471 | "node": ">=0.10.0" 1472 | } 1473 | }, 1474 | "node_modules/object-inspect": { 1475 | "version": "1.12.3", 1476 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", 1477 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", 1478 | "dev": true, 1479 | "funding": { 1480 | "url": "https://github.com/sponsors/ljharb" 1481 | } 1482 | }, 1483 | "node_modules/once": { 1484 | "version": "1.4.0", 1485 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1486 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1487 | "dev": true, 1488 | "dependencies": { 1489 | "wrappy": "1" 1490 | } 1491 | }, 1492 | "node_modules/opener": { 1493 | "version": "1.5.2", 1494 | "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", 1495 | "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", 1496 | "dev": true, 1497 | "bin": { 1498 | "opener": "bin/opener-bin.js" 1499 | } 1500 | }, 1501 | "node_modules/path-is-absolute": { 1502 | "version": "1.0.1", 1503 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1504 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1505 | "dev": true, 1506 | "engines": { 1507 | "node": ">=0.10.0" 1508 | } 1509 | }, 1510 | "node_modules/path-parse": { 1511 | "version": "1.0.7", 1512 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1513 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1514 | "dev": true 1515 | }, 1516 | "node_modules/pend": { 1517 | "version": "1.2.0", 1518 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 1519 | "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", 1520 | "dev": true 1521 | }, 1522 | "node_modules/picocolors": { 1523 | "version": "1.0.0", 1524 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1525 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1526 | "dev": true 1527 | }, 1528 | "node_modules/pify": { 1529 | "version": "2.3.0", 1530 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1531 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 1532 | "dev": true, 1533 | "engines": { 1534 | "node": ">=0.10.0" 1535 | } 1536 | }, 1537 | "node_modules/pinkie": { 1538 | "version": "2.0.4", 1539 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1540 | "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", 1541 | "dev": true, 1542 | "engines": { 1543 | "node": ">=0.10.0" 1544 | } 1545 | }, 1546 | "node_modules/pinkie-promise": { 1547 | "version": "2.0.1", 1548 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1549 | "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", 1550 | "dev": true, 1551 | "dependencies": { 1552 | "pinkie": "^2.0.0" 1553 | }, 1554 | "engines": { 1555 | "node": ">=0.10.0" 1556 | } 1557 | }, 1558 | "node_modules/portfinder": { 1559 | "version": "1.0.32", 1560 | "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", 1561 | "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", 1562 | "dev": true, 1563 | "dependencies": { 1564 | "async": "^2.6.4", 1565 | "debug": "^3.2.7", 1566 | "mkdirp": "^0.5.6" 1567 | }, 1568 | "engines": { 1569 | "node": ">= 0.12.0" 1570 | } 1571 | }, 1572 | "node_modules/prettier": { 1573 | "version": "3.2.4", 1574 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", 1575 | "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", 1576 | "dev": true, 1577 | "bin": { 1578 | "prettier": "bin/prettier.cjs" 1579 | }, 1580 | "engines": { 1581 | "node": ">=14" 1582 | }, 1583 | "funding": { 1584 | "url": "https://github.com/prettier/prettier?sponsor=1" 1585 | } 1586 | }, 1587 | "node_modules/prettier-plugin-jsdoc": { 1588 | "version": "1.3.0", 1589 | "resolved": "https://registry.npmjs.org/prettier-plugin-jsdoc/-/prettier-plugin-jsdoc-1.3.0.tgz", 1590 | "integrity": "sha512-cQm8xIa0fN9ieJFMXACQd6JPycl+8ouOijAqUqu44EF/s4fXL3Wi9sKXuEaodsEWgCN42Xby/bNhqgM1iWx4uw==", 1591 | "dev": true, 1592 | "dependencies": { 1593 | "binary-searching": "^2.0.5", 1594 | "comment-parser": "^1.4.0", 1595 | "mdast-util-from-markdown": "^2.0.0" 1596 | }, 1597 | "engines": { 1598 | "node": ">=14.13.1 || >=16.0.0" 1599 | }, 1600 | "peerDependencies": { 1601 | "prettier": "^3.0.0" 1602 | } 1603 | }, 1604 | "node_modules/process-nextick-args": { 1605 | "version": "2.0.1", 1606 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1607 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 1608 | "dev": true 1609 | }, 1610 | "node_modules/publint": { 1611 | "version": "0.2.7", 1612 | "resolved": "https://registry.npmjs.org/publint/-/publint-0.2.7.tgz", 1613 | "integrity": "sha512-tLU4ee3110BxWfAmCZggJmCUnYWgPTr0QLnx08sqpLYa8JHRiOudd+CgzdpfU5x5eOaW2WMkpmOrFshRFYK7Mw==", 1614 | "dev": true, 1615 | "dependencies": { 1616 | "npm-packlist": "^5.1.3", 1617 | "picocolors": "^1.0.0", 1618 | "sade": "^1.8.1" 1619 | }, 1620 | "bin": { 1621 | "publint": "lib/cli.js" 1622 | }, 1623 | "engines": { 1624 | "node": ">=16" 1625 | }, 1626 | "funding": { 1627 | "url": "https://bjornlu.com/sponsor" 1628 | } 1629 | }, 1630 | "node_modules/qs": { 1631 | "version": "6.11.1", 1632 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", 1633 | "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", 1634 | "dev": true, 1635 | "dependencies": { 1636 | "side-channel": "^1.0.4" 1637 | }, 1638 | "engines": { 1639 | "node": ">=0.6" 1640 | }, 1641 | "funding": { 1642 | "url": "https://github.com/sponsors/ljharb" 1643 | } 1644 | }, 1645 | "node_modules/readable-stream": { 1646 | "version": "2.3.8", 1647 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", 1648 | "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", 1649 | "dev": true, 1650 | "dependencies": { 1651 | "core-util-is": "~1.0.0", 1652 | "inherits": "~2.0.3", 1653 | "isarray": "~1.0.0", 1654 | "process-nextick-args": "~2.0.0", 1655 | "safe-buffer": "~5.1.1", 1656 | "string_decoder": "~1.1.1", 1657 | "util-deprecate": "~1.0.1" 1658 | } 1659 | }, 1660 | "node_modules/rechoir": { 1661 | "version": "0.6.2", 1662 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 1663 | "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", 1664 | "dev": true, 1665 | "dependencies": { 1666 | "resolve": "^1.1.6" 1667 | }, 1668 | "engines": { 1669 | "node": ">= 0.10" 1670 | } 1671 | }, 1672 | "node_modules/requires-port": { 1673 | "version": "1.0.0", 1674 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 1675 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", 1676 | "dev": true 1677 | }, 1678 | "node_modules/resolve": { 1679 | "version": "1.22.2", 1680 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", 1681 | "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", 1682 | "dev": true, 1683 | "dependencies": { 1684 | "is-core-module": "^2.11.0", 1685 | "path-parse": "^1.0.7", 1686 | "supports-preserve-symlinks-flag": "^1.0.0" 1687 | }, 1688 | "bin": { 1689 | "resolve": "bin/resolve" 1690 | }, 1691 | "funding": { 1692 | "url": "https://github.com/sponsors/ljharb" 1693 | } 1694 | }, 1695 | "node_modules/sade": { 1696 | "version": "1.8.1", 1697 | "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", 1698 | "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", 1699 | "dev": true, 1700 | "dependencies": { 1701 | "mri": "^1.1.0" 1702 | }, 1703 | "engines": { 1704 | "node": ">=6" 1705 | } 1706 | }, 1707 | "node_modules/safe-buffer": { 1708 | "version": "5.1.2", 1709 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1710 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1711 | "dev": true 1712 | }, 1713 | "node_modules/safer-buffer": { 1714 | "version": "2.1.2", 1715 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1716 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1717 | "dev": true 1718 | }, 1719 | "node_modules/secure-compare": { 1720 | "version": "3.0.1", 1721 | "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", 1722 | "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", 1723 | "dev": true 1724 | }, 1725 | "node_modules/seek-bzip": { 1726 | "version": "1.0.6", 1727 | "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", 1728 | "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", 1729 | "dev": true, 1730 | "dependencies": { 1731 | "commander": "^2.8.1" 1732 | }, 1733 | "bin": { 1734 | "seek-bunzip": "bin/seek-bunzip", 1735 | "seek-table": "bin/seek-bzip-table" 1736 | } 1737 | }, 1738 | "node_modules/shelljs": { 1739 | "version": "0.8.5", 1740 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", 1741 | "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", 1742 | "dev": true, 1743 | "dependencies": { 1744 | "glob": "^7.0.0", 1745 | "interpret": "^1.0.0", 1746 | "rechoir": "^0.6.2" 1747 | }, 1748 | "bin": { 1749 | "shjs": "bin/shjs" 1750 | }, 1751 | "engines": { 1752 | "node": ">=4" 1753 | } 1754 | }, 1755 | "node_modules/shelljs/node_modules/brace-expansion": { 1756 | "version": "1.1.11", 1757 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1758 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1759 | "dev": true, 1760 | "dependencies": { 1761 | "balanced-match": "^1.0.0", 1762 | "concat-map": "0.0.1" 1763 | } 1764 | }, 1765 | "node_modules/shelljs/node_modules/glob": { 1766 | "version": "7.2.3", 1767 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1768 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1769 | "dev": true, 1770 | "dependencies": { 1771 | "fs.realpath": "^1.0.0", 1772 | "inflight": "^1.0.4", 1773 | "inherits": "2", 1774 | "minimatch": "^3.1.1", 1775 | "once": "^1.3.0", 1776 | "path-is-absolute": "^1.0.0" 1777 | }, 1778 | "engines": { 1779 | "node": "*" 1780 | }, 1781 | "funding": { 1782 | "url": "https://github.com/sponsors/isaacs" 1783 | } 1784 | }, 1785 | "node_modules/shelljs/node_modules/minimatch": { 1786 | "version": "3.1.2", 1787 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1788 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1789 | "dev": true, 1790 | "dependencies": { 1791 | "brace-expansion": "^1.1.7" 1792 | }, 1793 | "engines": { 1794 | "node": "*" 1795 | } 1796 | }, 1797 | "node_modules/shx": { 1798 | "version": "0.3.4", 1799 | "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", 1800 | "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", 1801 | "dev": true, 1802 | "dependencies": { 1803 | "minimist": "^1.2.3", 1804 | "shelljs": "^0.8.5" 1805 | }, 1806 | "bin": { 1807 | "shx": "lib/cli.js" 1808 | }, 1809 | "engines": { 1810 | "node": ">=6" 1811 | } 1812 | }, 1813 | "node_modules/side-channel": { 1814 | "version": "1.0.4", 1815 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1816 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1817 | "dev": true, 1818 | "dependencies": { 1819 | "call-bind": "^1.0.0", 1820 | "get-intrinsic": "^1.0.2", 1821 | "object-inspect": "^1.9.0" 1822 | }, 1823 | "funding": { 1824 | "url": "https://github.com/sponsors/ljharb" 1825 | } 1826 | }, 1827 | "node_modules/string_decoder": { 1828 | "version": "1.1.1", 1829 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1830 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1831 | "dev": true, 1832 | "dependencies": { 1833 | "safe-buffer": "~5.1.0" 1834 | } 1835 | }, 1836 | "node_modules/strip-dirs": { 1837 | "version": "2.1.0", 1838 | "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", 1839 | "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", 1840 | "dev": true, 1841 | "dependencies": { 1842 | "is-natural-number": "^4.0.1" 1843 | } 1844 | }, 1845 | "node_modules/supports-color": { 1846 | "version": "7.2.0", 1847 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1848 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1849 | "dev": true, 1850 | "dependencies": { 1851 | "has-flag": "^4.0.0" 1852 | }, 1853 | "engines": { 1854 | "node": ">=8" 1855 | } 1856 | }, 1857 | "node_modules/supports-preserve-symlinks-flag": { 1858 | "version": "1.0.0", 1859 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1860 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1861 | "dev": true, 1862 | "engines": { 1863 | "node": ">= 0.4" 1864 | }, 1865 | "funding": { 1866 | "url": "https://github.com/sponsors/ljharb" 1867 | } 1868 | }, 1869 | "node_modules/tar-stream": { 1870 | "version": "1.6.2", 1871 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", 1872 | "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", 1873 | "dev": true, 1874 | "dependencies": { 1875 | "bl": "^1.0.0", 1876 | "buffer-alloc": "^1.2.0", 1877 | "end-of-stream": "^1.0.0", 1878 | "fs-constants": "^1.0.0", 1879 | "readable-stream": "^2.3.0", 1880 | "to-buffer": "^1.1.1", 1881 | "xtend": "^4.0.0" 1882 | }, 1883 | "engines": { 1884 | "node": ">= 0.8.0" 1885 | } 1886 | }, 1887 | "node_modules/through": { 1888 | "version": "2.3.8", 1889 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1890 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", 1891 | "dev": true 1892 | }, 1893 | "node_modules/to-buffer": { 1894 | "version": "1.1.1", 1895 | "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", 1896 | "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", 1897 | "dev": true 1898 | }, 1899 | "node_modules/unbzip2-stream": { 1900 | "version": "1.4.3", 1901 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", 1902 | "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", 1903 | "dev": true, 1904 | "dependencies": { 1905 | "buffer": "^5.2.1", 1906 | "through": "^2.3.8" 1907 | } 1908 | }, 1909 | "node_modules/union": { 1910 | "version": "0.5.0", 1911 | "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", 1912 | "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", 1913 | "dev": true, 1914 | "dependencies": { 1915 | "qs": "^6.4.0" 1916 | }, 1917 | "engines": { 1918 | "node": ">= 0.8.0" 1919 | } 1920 | }, 1921 | "node_modules/unist-util-stringify-position": { 1922 | "version": "4.0.0", 1923 | "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", 1924 | "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", 1925 | "dev": true, 1926 | "dependencies": { 1927 | "@types/unist": "^3.0.0" 1928 | }, 1929 | "funding": { 1930 | "type": "opencollective", 1931 | "url": "https://opencollective.com/unified" 1932 | } 1933 | }, 1934 | "node_modules/url-join": { 1935 | "version": "4.0.1", 1936 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", 1937 | "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", 1938 | "dev": true 1939 | }, 1940 | "node_modules/util-deprecate": { 1941 | "version": "1.0.2", 1942 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1943 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1944 | "dev": true 1945 | }, 1946 | "node_modules/web-streams-polyfill": { 1947 | "version": "3.2.1", 1948 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 1949 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", 1950 | "dev": true, 1951 | "engines": { 1952 | "node": ">= 8" 1953 | } 1954 | }, 1955 | "node_modules/whatwg-encoding": { 1956 | "version": "2.0.0", 1957 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", 1958 | "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", 1959 | "dev": true, 1960 | "dependencies": { 1961 | "iconv-lite": "0.6.3" 1962 | }, 1963 | "engines": { 1964 | "node": ">=12" 1965 | } 1966 | }, 1967 | "node_modules/wrappy": { 1968 | "version": "1.0.2", 1969 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1970 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1971 | "dev": true 1972 | }, 1973 | "node_modules/xtend": { 1974 | "version": "4.0.2", 1975 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1976 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1977 | "dev": true, 1978 | "engines": { 1979 | "node": ">=0.4" 1980 | } 1981 | }, 1982 | "node_modules/yauzl": { 1983 | "version": "2.10.0", 1984 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 1985 | "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", 1986 | "dev": true, 1987 | "dependencies": { 1988 | "buffer-crc32": "~0.2.3", 1989 | "fd-slicer": "~1.1.0" 1990 | } 1991 | } 1992 | } 1993 | } 1994 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@livestore/sqlite-wasm", 3 | "version": "3.46.0-build4", 4 | "description": "SQLite Wasm conveniently wrapped as an ES Module.", 5 | "keywords": [ 6 | "sqlite", 7 | "sqlite3", 8 | "sqlite-wasm", 9 | "sqlite3-wasm", 10 | "webassembly", 11 | "wasm", 12 | "esm", 13 | "opfs", 14 | "origin-private-file-system" 15 | ], 16 | "main": "index.mjs", 17 | "type": "module", 18 | "files": [ 19 | "index.d.ts", 20 | "index.mjs", 21 | "sqlite-wasm/" 22 | ], 23 | "types": "index.d.ts", 24 | "exports": { 25 | ".": { 26 | "types": "./index.d.ts", 27 | "import": "./index.mjs", 28 | "main": "./index.mjs", 29 | "node": "./sqlite-wasm/jswasm/sqlite3-node.mjs", 30 | "browser": "./index.mjs" 31 | }, 32 | "./node": { 33 | "types": "./index.d.ts", 34 | "default": "./sqlite-wasm/jswasm/sqlite3-node.mjs" 35 | }, 36 | "./package.json": "./package.json" 37 | }, 38 | "bin": { 39 | "sqlite-wasm": "bin/index.js" 40 | }, 41 | "scripts": { 42 | "publint": "npx publint", 43 | "clean": "shx rm -rf sqlite-wasm", 44 | "build": "echo Already manually built SQLite Wasm.", 45 | "start": "npx http-server --coop", 46 | "fix": "npx prettier . --write", 47 | "prepare": "npm run build && npm run fix && npm run publint", 48 | "deploy": "npm run prepare && git add . && git commit -am 'New release' && git push && npm publish --access public" 49 | }, 50 | "repository": { 51 | "type": "git", 52 | "url": "git+https://github.com/sqlite/sqlite-wasm.git" 53 | }, 54 | "author": "Johannes Schickling", 55 | "license": "Apache-2.0", 56 | "bugs": { 57 | "url": "https://github.com/sqlite/sqlite-wasm/issues" 58 | }, 59 | "homepage": "https://github.com/sqlite/sqlite-wasm#readme", 60 | "devDependencies": { 61 | "decompress": "^4.2.1", 62 | "http-server": "github:vapier/http-server", 63 | "module-workers-polyfill": "^0.3.2", 64 | "node-fetch": "^3.3.2", 65 | "prettier": "^3.2.4", 66 | "publint": "^0.2.7", 67 | "prettier-plugin-jsdoc": "^1.3.0", 68 | "shx": "^0.3.4" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sqlite-wasm/.gitignore: -------------------------------------------------------------------------------- 1 | speedtest* 2 | sqlite3-api* -------------------------------------------------------------------------------- /sqlite-wasm/jswasm/sqlite3-opfs-async-proxy.js: -------------------------------------------------------------------------------- 1 | /* 2 | 2022-09-16 3 | 4 | The author disclaims copyright to this source code. In place of a 5 | legal notice, here is a blessing: 6 | 7 | * May you do good and not evil. 8 | * May you find forgiveness for yourself and forgive others. 9 | * May you share freely, never taking more than you give. 10 | 11 | *********************************************************************** 12 | 13 | A Worker which manages asynchronous OPFS handles on behalf of a 14 | synchronous API which controls it via a combination of Worker 15 | messages, SharedArrayBuffer, and Atomics. It is the asynchronous 16 | counterpart of the API defined in sqlite3-vfs-opfs.js. 17 | 18 | Highly indebted to: 19 | 20 | https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/OriginPrivateFileSystemVFS.js 21 | 22 | for demonstrating how to use the OPFS APIs. 23 | 24 | This file is to be loaded as a Worker. It does not have any direct 25 | access to the sqlite3 JS/WASM bits, so any bits which it needs (most 26 | notably SQLITE_xxx integer codes) have to be imported into it via an 27 | initialization process. 28 | 29 | This file represents an implementation detail of a larger piece of 30 | code, and not a public interface. Its details may change at any time 31 | and are not intended to be used by any client-level code. 32 | 33 | 2022-11-27: Chrome v108 changes some async methods to synchronous, as 34 | documented at: 35 | 36 | https://developer.chrome.com/blog/sync-methods-for-accesshandles/ 37 | 38 | Firefox v111 and Safari 16.4, both released in March 2023, also 39 | include this. 40 | 41 | We cannot change to the sync forms at this point without breaking 42 | clients who use Chrome v104-ish or higher. truncate(), getSize(), 43 | flush(), and close() are now (as of v108) synchronous. Calling them 44 | with an "await", as we have to for the async forms, is still legal 45 | with the sync forms but is superfluous. Calling the async forms with 46 | theFunc().then(...) is not compatible with the change to 47 | synchronous, but we do do not use those APIs that way. i.e. we don't 48 | _need_ to change anything for this, but at some point (after Chrome 49 | versions (approximately) 104-107 are extinct) should change our 50 | usage of those methods to remove the "await". 51 | */ 52 | 'use strict'; 53 | const wPost = (type, ...args) => postMessage({ type, payload: args }); 54 | const installAsyncProxy = function (self) { 55 | const toss = function (...args) { 56 | throw new Error(args.join(' ')); 57 | }; 58 | if (globalThis.window === globalThis) { 59 | toss( 60 | 'This code cannot run from the main thread.', 61 | 'Load it as a Worker from a separate Worker.', 62 | ); 63 | } else if (!navigator?.storage?.getDirectory) { 64 | toss('This API requires navigator.storage.getDirectory.'); 65 | } 66 | 67 | /** Will hold state copied to this object from the syncronous side of this API. */ 68 | const state = Object.create(null); 69 | 70 | /** 71 | * Verbose: 72 | * 73 | * 0 = no logging output 74 | * 1 = only errors 75 | * 2 = warnings and errors 76 | * 3 = debug, warnings, and errors 77 | */ 78 | state.verbose = 1; 79 | 80 | const loggers = { 81 | 0: console.error.bind(console), 82 | 1: console.warn.bind(console), 83 | 2: console.log.bind(console), 84 | }; 85 | const logImpl = (level, ...args) => { 86 | if (state.verbose > level) loggers[level]('OPFS asyncer:', ...args); 87 | }; 88 | const log = (...args) => logImpl(2, ...args); 89 | const warn = (...args) => logImpl(1, ...args); 90 | const error = (...args) => logImpl(0, ...args); 91 | const metrics = Object.create(null); 92 | metrics.reset = () => { 93 | let k; 94 | const r = (m) => (m.count = m.time = m.wait = 0); 95 | for (k in state.opIds) { 96 | r((metrics[k] = Object.create(null))); 97 | } 98 | let s = (metrics.s11n = Object.create(null)); 99 | s = s.serialize = Object.create(null); 100 | s.count = s.time = 0; 101 | s = metrics.s11n.deserialize = Object.create(null); 102 | s.count = s.time = 0; 103 | }; 104 | metrics.dump = () => { 105 | let k, 106 | n = 0, 107 | t = 0, 108 | w = 0; 109 | for (k in state.opIds) { 110 | const m = metrics[k]; 111 | n += m.count; 112 | t += m.time; 113 | w += m.wait; 114 | m.avgTime = m.count && m.time ? m.time / m.count : 0; 115 | } 116 | console.log( 117 | globalThis?.location?.href, 118 | 'metrics for', 119 | globalThis?.location?.href, 120 | ':\n', 121 | metrics, 122 | '\nTotal of', 123 | n, 124 | 'op(s) for', 125 | t, 126 | 'ms', 127 | 'approx', 128 | w, 129 | 'ms spent waiting on OPFS APIs.', 130 | ); 131 | console.log('Serialization metrics:', metrics.s11n); 132 | }; 133 | 134 | /** 135 | * __openFiles is a map of sqlite3_file pointers (integers) to metadata 136 | * related to a given OPFS file handles. The pointers are, in this side of the 137 | * interface, opaque file handle IDs provided by the synchronous part of this 138 | * constellation. Each value is an object with a structure demonstrated in the 139 | * xOpen() impl. 140 | */ 141 | const __openFiles = Object.create(null); 142 | /** 143 | * __implicitLocks is a Set of sqlite3_file pointers (integers) which were 144 | * "auto-locked". i.e. those for which we obtained a sync access handle 145 | * without an explicit xLock() call. Such locks will be released during db 146 | * connection idle time, whereas a sync access handle obtained via xLock(), or 147 | * subsequently xLock()'d after auto-acquisition, will not be released until 148 | * xUnlock() is called. 149 | * 150 | * Maintenance reminder: if we relinquish auto-locks at the end of the 151 | * operation which acquires them, we pay a massive performance 152 | * penalty: speedtest1 benchmarks take up to 4x as long. By delaying 153 | * the lock release until idle time, the hit is negligible. 154 | */ 155 | const __implicitLocks = new Set(); 156 | 157 | /** 158 | * Expects an OPFS file path. It gets resolved, such that ".." components are 159 | * properly expanded, and returned. If the 2nd arg is true, the result is 160 | * returned as an array of path elements, else an absolute path string is 161 | * returned. 162 | */ 163 | const getResolvedPath = function (filename, splitIt) { 164 | const p = new URL(filename, 'file://irrelevant').pathname; 165 | return splitIt ? p.split('/').filter((v) => !!v) : p; 166 | }; 167 | 168 | /** 169 | * Takes the absolute path to a filesystem element. Returns an array of 170 | * [handleOfContainingDir, filename]. If the 2nd argument is truthy then each 171 | * directory element leading to the file is created along the way. Throws if 172 | * any creation or resolution fails. 173 | */ 174 | const getDirForFilename = async function f(absFilename, createDirs = false) { 175 | const path = getResolvedPath(absFilename, true); 176 | const filename = path.pop(); 177 | let dh = state.rootDir; 178 | for (const dirName of path) { 179 | if (dirName) { 180 | dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs }); 181 | } 182 | } 183 | return [dh, filename]; 184 | }; 185 | 186 | /** 187 | * If the given file-holding object has a sync handle attached to it, that 188 | * handle is remove and asynchronously closed. Though it may sound sensible to 189 | * continue work as soon as the close() returns (noting that it's 190 | * asynchronous), doing so can cause operations performed soon afterwards, 191 | * e.g. a call to getSyncHandle() to fail because they may happen out of order 192 | * from the close(). OPFS does not guaranty that the actual order of 193 | * operations is retained in such cases. i.e. always "await" on the result of 194 | * this function. 195 | */ 196 | const closeSyncHandle = async (fh) => { 197 | if (fh.syncHandle) { 198 | log('Closing sync handle for', fh.filenameAbs); 199 | const h = fh.syncHandle; 200 | delete fh.syncHandle; 201 | delete fh.xLock; 202 | __implicitLocks.delete(fh.fid); 203 | return h.close(); 204 | } 205 | }; 206 | 207 | /** 208 | * A proxy for closeSyncHandle() which is guaranteed to not throw. 209 | * 210 | * This function is part of a lock/unlock step in functions which 211 | * require a sync access handle but may be called without xLock() 212 | * having been called first. Such calls need to release that 213 | * handle to avoid locking the file for all of time. This is an 214 | * _attempt_ at reducing cross-tab contention but it may prove 215 | * to be more of a problem than a solution and may need to be 216 | * removed. 217 | */ 218 | const closeSyncHandleNoThrow = async (fh) => { 219 | try { 220 | await closeSyncHandle(fh); 221 | } catch (e) { 222 | warn('closeSyncHandleNoThrow() ignoring:', e, fh); 223 | } 224 | }; 225 | 226 | /* Release all auto-locks. */ 227 | const releaseImplicitLocks = async () => { 228 | if (__implicitLocks.size) { 229 | /* Release all auto-locks. */ 230 | for (const fid of __implicitLocks) { 231 | const fh = __openFiles[fid]; 232 | await closeSyncHandleNoThrow(fh); 233 | log('Auto-unlocked', fid, fh.filenameAbs); 234 | } 235 | } 236 | }; 237 | 238 | /** 239 | * An experiment in improving concurrency by freeing up implicit locks sooner. 240 | * This is known to impact performance dramatically but it has also shown to 241 | * improve concurrency considerably. 242 | * 243 | * If fh.releaseImplicitLocks is truthy and fh is in __implicitLocks, 244 | * this routine returns closeSyncHandleNoThrow(), else it is a no-op. 245 | */ 246 | const releaseImplicitLock = async (fh) => { 247 | if (fh.releaseImplicitLocks && __implicitLocks.has(fh.fid)) { 248 | return closeSyncHandleNoThrow(fh); 249 | } 250 | }; 251 | 252 | /** 253 | * An error class specifically for use with getSyncHandle(), the goal of which 254 | * is to eventually be able to distinguish unambiguously between 255 | * locking-related failures and other types, noting that we cannot currently 256 | * do so because createSyncAccessHandle() does not define its exceptions in 257 | * the required level of detail. 258 | * 259 | * 2022-11-29: according to: 260 | * 261 | * https://github.com/whatwg/fs/pull/21 262 | * 263 | * NoModificationAllowedError will be the standard exception thrown 264 | * when acquisition of a sync access handle fails due to a locking 265 | * error. As of this writing, that error type is not visible in the 266 | * dev console in Chrome v109, nor is it documented in MDN, but an 267 | * error with that "name" property is being thrown from the OPFS 268 | * layer. 269 | */ 270 | class GetSyncHandleError extends Error { 271 | constructor(errorObject, ...msg) { 272 | super( 273 | [...msg, ': ' + errorObject.name + ':', errorObject.message].join(' '), 274 | { 275 | cause: errorObject, 276 | }, 277 | ); 278 | this.name = 'GetSyncHandleError'; 279 | } 280 | } 281 | GetSyncHandleError.convertRc = (e, rc) => { 282 | if (1) { 283 | return e instanceof GetSyncHandleError && 284 | (e.cause.name === 'NoModificationAllowedError' || 285 | /* Inconsistent exception.name from Chrome/ium with the 286 | same exception.message text: */ 287 | (e.cause.name === 'DOMException' && 288 | 0 === e.cause.message.indexOf('Access Handles cannot'))) 289 | ? /*console.warn("SQLITE_BUSY",e),*/ 290 | state.sq3Codes.SQLITE_BUSY 291 | : rc; 292 | } else { 293 | return rc; 294 | } 295 | }; 296 | /** 297 | * Returns the sync access handle associated with the given file handle object 298 | * (which must be a valid handle object, as created by xOpen()), lazily 299 | * opening it if needed. 300 | * 301 | * In order to help alleviate cross-tab contention for a dabase, if 302 | * an exception is thrown while acquiring the handle, this routine 303 | * will wait briefly and try again, up to some fixed number of 304 | * times. If acquisition still fails at that point it will give up 305 | * and propagate the exception. Client-level code will see that as 306 | * an I/O error. 307 | */ 308 | const getSyncHandle = async (fh, opName) => { 309 | if (!fh.syncHandle) { 310 | const t = performance.now(); 311 | log('Acquiring sync handle for', fh.filenameAbs); 312 | const maxTries = 6, 313 | msBase = state.asyncIdleWaitTime * 2; 314 | let i = 1, 315 | ms = msBase; 316 | for (; true; ms = msBase * ++i) { 317 | try { 318 | //if(i<3) toss("Just testing getSyncHandle() wait-and-retry."); 319 | //TODO? A config option which tells it to throw here 320 | //randomly every now and then, for testing purposes. 321 | fh.syncHandle = await fh.fileHandle.createSyncAccessHandle(); 322 | break; 323 | } catch (e) { 324 | if (i === maxTries) { 325 | throw new GetSyncHandleError( 326 | e, 327 | 'Error getting sync handle for', 328 | opName + '().', 329 | maxTries, 330 | 'attempts failed.', 331 | fh.filenameAbs, 332 | ); 333 | } 334 | warn( 335 | 'Error getting sync handle for', 336 | opName + '(). Waiting', 337 | ms, 338 | 'ms and trying again.', 339 | fh.filenameAbs, 340 | e, 341 | ); 342 | Atomics.wait(state.sabOPView, state.opIds.retry, 0, ms); 343 | } 344 | } 345 | log( 346 | 'Got', 347 | opName + '() sync handle for', 348 | fh.filenameAbs, 349 | 'in', 350 | performance.now() - t, 351 | 'ms', 352 | ); 353 | if (!fh.xLock) { 354 | __implicitLocks.add(fh.fid); 355 | log( 356 | 'Acquired implicit lock for', 357 | opName + '()', 358 | fh.fid, 359 | fh.filenameAbs, 360 | ); 361 | } 362 | } 363 | return fh.syncHandle; 364 | }; 365 | 366 | /** 367 | * Stores the given value at state.sabOPView[state.opIds.rc] and then 368 | * Atomics.notify()'s it. 369 | */ 370 | const storeAndNotify = (opName, value) => { 371 | log(opName + '() => notify(', value, ')'); 372 | Atomics.store(state.sabOPView, state.opIds.rc, value); 373 | Atomics.notify(state.sabOPView, state.opIds.rc); 374 | }; 375 | 376 | /** Throws if fh is a file-holding object which is flagged as read-only. */ 377 | const affirmNotRO = function (opName, fh) { 378 | if (fh.readOnly) toss(opName + '(): File is read-only: ' + fh.filenameAbs); 379 | }; 380 | 381 | /** 382 | * We track 2 different timers: the "metrics" timer records how much time we 383 | * spend performing work. The "wait" timer records how much time we spend 384 | * waiting on the underlying OPFS timer. See the calls to mTimeStart(), 385 | * mTimeEnd(), wTimeStart(), and wTimeEnd() throughout this file to see how 386 | * they're used. 387 | */ 388 | const __mTimer = Object.create(null); 389 | __mTimer.op = undefined; 390 | __mTimer.start = undefined; 391 | const mTimeStart = (op) => { 392 | __mTimer.start = performance.now(); 393 | __mTimer.op = op; 394 | //metrics[op] || toss("Maintenance required: missing metrics for",op); 395 | ++metrics[op].count; 396 | }; 397 | const mTimeEnd = () => 398 | (metrics[__mTimer.op].time += performance.now() - __mTimer.start); 399 | const __wTimer = Object.create(null); 400 | __wTimer.op = undefined; 401 | __wTimer.start = undefined; 402 | const wTimeStart = (op) => { 403 | __wTimer.start = performance.now(); 404 | __wTimer.op = op; 405 | //metrics[op] || toss("Maintenance required: missing metrics for",op); 406 | }; 407 | const wTimeEnd = () => 408 | (metrics[__wTimer.op].wait += performance.now() - __wTimer.start); 409 | 410 | /** 411 | * Gets set to true by the 'opfs-async-shutdown' command to quit the wait 412 | * loop. This is only intended for debugging purposes: we cannot inspect this 413 | * file's state while the tight waitLoop() is running and need a way to stop 414 | * that loop for introspection purposes. 415 | */ 416 | let flagAsyncShutdown = false; 417 | 418 | /** 419 | * Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods methods, as 420 | * well as helpers like mkdir(). Maintenance reminder: members are in 421 | * alphabetical order to simplify finding them. 422 | */ 423 | const vfsAsyncImpls = { 424 | 'opfs-async-metrics': async () => { 425 | mTimeStart('opfs-async-metrics'); 426 | metrics.dump(); 427 | storeAndNotify('opfs-async-metrics', 0); 428 | mTimeEnd(); 429 | }, 430 | 'opfs-async-shutdown': async () => { 431 | flagAsyncShutdown = true; 432 | storeAndNotify('opfs-async-shutdown', 0); 433 | }, 434 | mkdir: async (dirname) => { 435 | mTimeStart('mkdir'); 436 | let rc = 0; 437 | wTimeStart('mkdir'); 438 | try { 439 | await getDirForFilename(dirname + '/filepart', true); 440 | } catch (e) { 441 | state.s11n.storeException(2, e); 442 | rc = state.sq3Codes.SQLITE_IOERR; 443 | } finally { 444 | wTimeEnd(); 445 | } 446 | storeAndNotify('mkdir', rc); 447 | mTimeEnd(); 448 | }, 449 | xAccess: async (filename) => { 450 | mTimeStart('xAccess'); 451 | /* OPFS cannot support the full range of xAccess() queries 452 | sqlite3 calls for. We can essentially just tell if the file 453 | is accessible, but if it is then it's automatically writable 454 | (unless it's locked, which we cannot(?) know without trying 455 | to open it). OPFS does not have the notion of read-only. 456 | 457 | The return semantics of this function differ from sqlite3's 458 | xAccess semantics because we are limited in what we can 459 | communicate back to our synchronous communication partner: 0 = 460 | accessible, non-0 means not accessible. 461 | */ 462 | let rc = 0; 463 | wTimeStart('xAccess'); 464 | try { 465 | const [dh, fn] = await getDirForFilename(filename); 466 | await dh.getFileHandle(fn); 467 | } catch (e) { 468 | state.s11n.storeException(2, e); 469 | rc = state.sq3Codes.SQLITE_IOERR; 470 | } finally { 471 | wTimeEnd(); 472 | } 473 | storeAndNotify('xAccess', rc); 474 | mTimeEnd(); 475 | }, 476 | xClose: async function (fid /*sqlite3_file pointer*/) { 477 | const opName = 'xClose'; 478 | mTimeStart(opName); 479 | __implicitLocks.delete(fid); 480 | const fh = __openFiles[fid]; 481 | let rc = 0; 482 | wTimeStart(opName); 483 | if (fh) { 484 | delete __openFiles[fid]; 485 | await closeSyncHandle(fh); 486 | if (fh.deleteOnClose) { 487 | try { 488 | await fh.dirHandle.removeEntry(fh.filenamePart); 489 | } catch (e) { 490 | warn('Ignoring dirHandle.removeEntry() failure of', fh, e); 491 | } 492 | } 493 | } else { 494 | state.s11n.serialize(); 495 | rc = state.sq3Codes.SQLITE_NOTFOUND; 496 | } 497 | wTimeEnd(); 498 | storeAndNotify(opName, rc); 499 | mTimeEnd(); 500 | }, 501 | xDelete: async function (...args) { 502 | mTimeStart('xDelete'); 503 | const rc = await vfsAsyncImpls.xDeleteNoWait(...args); 504 | storeAndNotify('xDelete', rc); 505 | mTimeEnd(); 506 | }, 507 | xDeleteNoWait: async function (filename, syncDir = 0, recursive = false) { 508 | /* The syncDir flag is, for purposes of the VFS API's semantics, 509 | ignored here. However, if it has the value 0x1234 then: after 510 | deleting the given file, recursively try to delete any empty 511 | directories left behind in its wake (ignoring any errors and 512 | stopping at the first failure). 513 | 514 | That said: we don't know for sure that removeEntry() fails if 515 | the dir is not empty because the API is not documented. It has, 516 | however, a "recursive" flag which defaults to false, so 517 | presumably it will fail if the dir is not empty and that flag 518 | is false. 519 | */ 520 | let rc = 0; 521 | wTimeStart('xDelete'); 522 | try { 523 | while (filename) { 524 | const [hDir, filenamePart] = await getDirForFilename(filename, false); 525 | if (!filenamePart) break; 526 | await hDir.removeEntry(filenamePart, { recursive }); 527 | if (0x1234 !== syncDir) break; 528 | recursive = false; 529 | filename = getResolvedPath(filename, true); 530 | filename.pop(); 531 | filename = filename.join('/'); 532 | } 533 | } catch (e) { 534 | state.s11n.storeException(2, e); 535 | rc = state.sq3Codes.SQLITE_IOERR_DELETE; 536 | } 537 | wTimeEnd(); 538 | return rc; 539 | }, 540 | xFileSize: async function (fid /*sqlite3_file pointer*/) { 541 | mTimeStart('xFileSize'); 542 | const fh = __openFiles[fid]; 543 | let rc = 0; 544 | wTimeStart('xFileSize'); 545 | try { 546 | const sz = await (await getSyncHandle(fh, 'xFileSize')).getSize(); 547 | state.s11n.serialize(Number(sz)); 548 | } catch (e) { 549 | state.s11n.storeException(1, e); 550 | rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR); 551 | } 552 | await releaseImplicitLock(fh); 553 | wTimeEnd(); 554 | storeAndNotify('xFileSize', rc); 555 | mTimeEnd(); 556 | }, 557 | xLock: async function ( 558 | fid /*sqlite3_file pointer*/, 559 | lockType /*SQLITE_LOCK_...*/, 560 | ) { 561 | mTimeStart('xLock'); 562 | const fh = __openFiles[fid]; 563 | let rc = 0; 564 | const oldLockType = fh.xLock; 565 | fh.xLock = lockType; 566 | if (!fh.syncHandle) { 567 | wTimeStart('xLock'); 568 | try { 569 | await getSyncHandle(fh, 'xLock'); 570 | __implicitLocks.delete(fid); 571 | } catch (e) { 572 | state.s11n.storeException(1, e); 573 | rc = GetSyncHandleError.convertRc( 574 | e, 575 | state.sq3Codes.SQLITE_IOERR_LOCK, 576 | ); 577 | fh.xLock = oldLockType; 578 | } 579 | wTimeEnd(); 580 | } 581 | storeAndNotify('xLock', rc); 582 | mTimeEnd(); 583 | }, 584 | xOpen: async function ( 585 | fid /*sqlite3_file pointer*/, 586 | filename, 587 | flags /*SQLITE_OPEN_...*/, 588 | opfsFlags /*OPFS_...*/, 589 | ) { 590 | const opName = 'xOpen'; 591 | mTimeStart(opName); 592 | const create = state.sq3Codes.SQLITE_OPEN_CREATE & flags; 593 | wTimeStart('xOpen'); 594 | try { 595 | let hDir, filenamePart; 596 | try { 597 | [hDir, filenamePart] = await getDirForFilename(filename, !!create); 598 | } catch (e) { 599 | state.s11n.storeException(1, e); 600 | storeAndNotify(opName, state.sq3Codes.SQLITE_NOTFOUND); 601 | mTimeEnd(); 602 | wTimeEnd(); 603 | return; 604 | } 605 | const hFile = await hDir.getFileHandle(filenamePart, { create }); 606 | wTimeEnd(); 607 | const fh = Object.assign(Object.create(null), { 608 | fid: fid, 609 | filenameAbs: filename, 610 | filenamePart: filenamePart, 611 | dirHandle: hDir, 612 | fileHandle: hFile, 613 | sabView: state.sabFileBufView, 614 | readOnly: create 615 | ? false 616 | : state.sq3Codes.SQLITE_OPEN_READONLY & flags, 617 | deleteOnClose: !!(state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags), 618 | }); 619 | fh.releaseImplicitLocks = 620 | opfsFlags & state.opfsFlags.OPFS_UNLOCK_ASAP || 621 | state.opfsFlags.defaultUnlockAsap; 622 | if ( 623 | 0 /* this block is modelled after something wa-sqlite 624 | does but it leads to immediate contention on journal files. 625 | Update: this approach reportedly only works for DELETE journal 626 | mode. */ && 627 | 0 === (flags & state.sq3Codes.SQLITE_OPEN_MAIN_DB) 628 | ) { 629 | /* sqlite does not lock these files, so go ahead and grab an OPFS 630 | lock. */ 631 | fh.xLock = 'xOpen' /* Truthy value to keep entry from getting 632 | flagged as auto-locked. String value so 633 | that we can easily distinguish is later 634 | if needed. */; 635 | await getSyncHandle(fh, 'xOpen'); 636 | } 637 | __openFiles[fid] = fh; 638 | storeAndNotify(opName, 0); 639 | } catch (e) { 640 | wTimeEnd(); 641 | error(opName, e); 642 | state.s11n.storeException(1, e); 643 | storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR); 644 | } 645 | mTimeEnd(); 646 | }, 647 | xRead: async function (fid /*sqlite3_file pointer*/, n, offset64) { 648 | mTimeStart('xRead'); 649 | let rc = 0, 650 | nRead; 651 | const fh = __openFiles[fid]; 652 | try { 653 | wTimeStart('xRead'); 654 | nRead = (await getSyncHandle(fh, 'xRead')).read( 655 | fh.sabView.subarray(0, n), 656 | { at: Number(offset64) }, 657 | ); 658 | wTimeEnd(); 659 | if (nRead < n) { 660 | /* Zero-fill remaining bytes */ 661 | fh.sabView.fill(0, nRead, n); 662 | rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; 663 | } 664 | } catch (e) { 665 | if (undefined === nRead) wTimeEnd(); 666 | error('xRead() failed', e, fh); 667 | state.s11n.storeException(1, e); 668 | rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_READ); 669 | } 670 | await releaseImplicitLock(fh); 671 | storeAndNotify('xRead', rc); 672 | mTimeEnd(); 673 | }, 674 | xSync: async function (fid /*sqlite3_file pointer*/, flags /*ignored*/) { 675 | mTimeStart('xSync'); 676 | const fh = __openFiles[fid]; 677 | let rc = 0; 678 | if (!fh.readOnly && fh.syncHandle) { 679 | try { 680 | wTimeStart('xSync'); 681 | await fh.syncHandle.flush(); 682 | } catch (e) { 683 | state.s11n.storeException(2, e); 684 | rc = state.sq3Codes.SQLITE_IOERR_FSYNC; 685 | } 686 | wTimeEnd(); 687 | } 688 | storeAndNotify('xSync', rc); 689 | mTimeEnd(); 690 | }, 691 | xTruncate: async function (fid /*sqlite3_file pointer*/, size) { 692 | mTimeStart('xTruncate'); 693 | let rc = 0; 694 | const fh = __openFiles[fid]; 695 | wTimeStart('xTruncate'); 696 | try { 697 | affirmNotRO('xTruncate', fh); 698 | await (await getSyncHandle(fh, 'xTruncate')).truncate(size); 699 | } catch (e) { 700 | error('xTruncate():', e, fh); 701 | state.s11n.storeException(2, e); 702 | rc = GetSyncHandleError.convertRc( 703 | e, 704 | state.sq3Codes.SQLITE_IOERR_TRUNCATE, 705 | ); 706 | } 707 | await releaseImplicitLock(fh); 708 | wTimeEnd(); 709 | storeAndNotify('xTruncate', rc); 710 | mTimeEnd(); 711 | }, 712 | xUnlock: async function ( 713 | fid /*sqlite3_file pointer*/, 714 | lockType /*SQLITE_LOCK_...*/, 715 | ) { 716 | mTimeStart('xUnlock'); 717 | let rc = 0; 718 | const fh = __openFiles[fid]; 719 | if (state.sq3Codes.SQLITE_LOCK_NONE === lockType && fh.syncHandle) { 720 | wTimeStart('xUnlock'); 721 | try { 722 | await closeSyncHandle(fh); 723 | } catch (e) { 724 | state.s11n.storeException(1, e); 725 | rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; 726 | } 727 | wTimeEnd(); 728 | } 729 | storeAndNotify('xUnlock', rc); 730 | mTimeEnd(); 731 | }, 732 | xWrite: async function (fid /*sqlite3_file pointer*/, n, offset64) { 733 | mTimeStart('xWrite'); 734 | let rc; 735 | const fh = __openFiles[fid]; 736 | wTimeStart('xWrite'); 737 | try { 738 | affirmNotRO('xWrite', fh); 739 | rc = 740 | n === 741 | (await getSyncHandle(fh, 'xWrite')).write(fh.sabView.subarray(0, n), { 742 | at: Number(offset64), 743 | }) 744 | ? 0 745 | : state.sq3Codes.SQLITE_IOERR_WRITE; 746 | } catch (e) { 747 | error('xWrite():', e, fh); 748 | state.s11n.storeException(1, e); 749 | rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_WRITE); 750 | } 751 | await releaseImplicitLock(fh); 752 | wTimeEnd(); 753 | storeAndNotify('xWrite', rc); 754 | mTimeEnd(); 755 | }, 756 | }; /*vfsAsyncImpls*/ 757 | 758 | const initS11n = () => { 759 | /** 760 | * ACHTUNG: this code is 100% duplicated in the other half of this proxy! 761 | * The documentation is maintained in the "synchronous half". 762 | */ 763 | if (state.s11n) return state.s11n; 764 | const textDecoder = new TextDecoder(), 765 | textEncoder = new TextEncoder('utf-8'), 766 | viewU8 = new Uint8Array( 767 | state.sabIO, 768 | state.sabS11nOffset, 769 | state.sabS11nSize, 770 | ), 771 | viewDV = new DataView( 772 | state.sabIO, 773 | state.sabS11nOffset, 774 | state.sabS11nSize, 775 | ); 776 | state.s11n = Object.create(null); 777 | const TypeIds = Object.create(null); 778 | TypeIds.number = { 779 | id: 1, 780 | size: 8, 781 | getter: 'getFloat64', 782 | setter: 'setFloat64', 783 | }; 784 | TypeIds.bigint = { 785 | id: 2, 786 | size: 8, 787 | getter: 'getBigInt64', 788 | setter: 'setBigInt64', 789 | }; 790 | TypeIds.boolean = { 791 | id: 3, 792 | size: 4, 793 | getter: 'getInt32', 794 | setter: 'setInt32', 795 | }; 796 | TypeIds.string = { id: 4 }; 797 | const getTypeId = (v) => 798 | TypeIds[typeof v] || 799 | toss('Maintenance required: this value type cannot be serialized.', v); 800 | const getTypeIdById = (tid) => { 801 | switch (tid) { 802 | case TypeIds.number.id: 803 | return TypeIds.number; 804 | case TypeIds.bigint.id: 805 | return TypeIds.bigint; 806 | case TypeIds.boolean.id: 807 | return TypeIds.boolean; 808 | case TypeIds.string.id: 809 | return TypeIds.string; 810 | default: 811 | toss('Invalid type ID:', tid); 812 | } 813 | }; 814 | state.s11n.deserialize = function (clear = false) { 815 | ++metrics.s11n.deserialize.count; 816 | const t = performance.now(); 817 | const argc = viewU8[0]; 818 | const rc = argc ? [] : null; 819 | if (argc) { 820 | const typeIds = []; 821 | let offset = 1, 822 | i, 823 | n, 824 | v; 825 | for (i = 0; i < argc; ++i, ++offset) { 826 | typeIds.push(getTypeIdById(viewU8[offset])); 827 | } 828 | for (i = 0; i < argc; ++i) { 829 | const t = typeIds[i]; 830 | if (t.getter) { 831 | v = viewDV[t.getter](offset, state.littleEndian); 832 | offset += t.size; 833 | } else { 834 | /*String*/ 835 | n = viewDV.getInt32(offset, state.littleEndian); 836 | offset += 4; 837 | v = textDecoder.decode(viewU8.slice(offset, offset + n)); 838 | offset += n; 839 | } 840 | rc.push(v); 841 | } 842 | } 843 | if (clear) viewU8[0] = 0; 844 | //log("deserialize:",argc, rc); 845 | metrics.s11n.deserialize.time += performance.now() - t; 846 | return rc; 847 | }; 848 | state.s11n.serialize = function (...args) { 849 | const t = performance.now(); 850 | ++metrics.s11n.serialize.count; 851 | if (args.length) { 852 | //log("serialize():",args); 853 | const typeIds = []; 854 | let i = 0, 855 | offset = 1; 856 | viewU8[0] = args.length & 0xff /* header = # of args */; 857 | for (; i < args.length; ++i, ++offset) { 858 | /* Write the TypeIds.id value into the next args.length 859 | bytes. */ 860 | typeIds.push(getTypeId(args[i])); 861 | viewU8[offset] = typeIds[i].id; 862 | } 863 | for (i = 0; i < args.length; ++i) { 864 | /* Deserialize the following bytes based on their 865 | corresponding TypeIds.id from the header. */ 866 | const t = typeIds[i]; 867 | if (t.setter) { 868 | viewDV[t.setter](offset, args[i], state.littleEndian); 869 | offset += t.size; 870 | } else { 871 | /*String*/ 872 | const s = textEncoder.encode(args[i]); 873 | viewDV.setInt32(offset, s.byteLength, state.littleEndian); 874 | offset += 4; 875 | viewU8.set(s, offset); 876 | offset += s.byteLength; 877 | } 878 | } 879 | //log("serialize() result:",viewU8.slice(0,offset)); 880 | } else { 881 | viewU8[0] = 0; 882 | } 883 | metrics.s11n.serialize.time += performance.now() - t; 884 | }; 885 | 886 | state.s11n.storeException = state.asyncS11nExceptions 887 | ? (priority, e) => { 888 | if (priority <= state.asyncS11nExceptions) { 889 | state.s11n.serialize([e.name, ': ', e.message].join('')); 890 | } 891 | } 892 | : () => {}; 893 | 894 | return state.s11n; 895 | }; /*initS11n()*/ 896 | 897 | const waitLoop = async function f() { 898 | const opHandlers = Object.create(null); 899 | for (let k of Object.keys(state.opIds)) { 900 | const vi = vfsAsyncImpls[k]; 901 | if (!vi) continue; 902 | const o = Object.create(null); 903 | opHandlers[state.opIds[k]] = o; 904 | o.key = k; 905 | o.f = vi; 906 | } 907 | while (!flagAsyncShutdown) { 908 | try { 909 | if ( 910 | 'not-equal' !== 911 | Atomics.wait( 912 | state.sabOPView, 913 | state.opIds.whichOp, 914 | 0, 915 | state.asyncIdleWaitTime, 916 | ) 917 | ) { 918 | /* Maintenance note: we compare against 'not-equal' because 919 | 920 | https://github.com/tomayac/sqlite-wasm/issues/12 921 | 922 | is reporting that this occassionally, under high loads, 923 | returns 'ok', which leads to the whichOp being 0 (which 924 | isn't a valid operation ID and leads to an exception, 925 | along with a corresponding ugly console log 926 | message). Unfortunately, the conditions for that cannot 927 | be reliably reproduced. The only place in our code which 928 | writes a 0 to the state.opIds.whichOp SharedArrayBuffer 929 | index is a few lines down from here, and that instance 930 | is required in order for clear communication between 931 | the sync half of this proxy and this half. 932 | */ 933 | await releaseImplicitLocks(); 934 | continue; 935 | } 936 | const opId = Atomics.load(state.sabOPView, state.opIds.whichOp); 937 | Atomics.store(state.sabOPView, state.opIds.whichOp, 0); 938 | const hnd = 939 | opHandlers[opId] ?? toss('No waitLoop handler for whichOp #', opId); 940 | const args = 941 | state.s11n.deserialize( 942 | true /* clear s11n to keep the caller from confusing this with 943 | an exception string written by the upcoming 944 | operation */, 945 | ) || []; 946 | //warn("waitLoop() whichOp =",opId, hnd, args); 947 | if (hnd.f) await hnd.f(...args); 948 | else error('Missing callback for opId', opId); 949 | } catch (e) { 950 | error('in waitLoop():', e); 951 | } 952 | } 953 | }; 954 | 955 | navigator.storage 956 | .getDirectory() 957 | .then(function (d) { 958 | state.rootDir = d; 959 | globalThis.onmessage = function ({ data }) { 960 | switch (data.type) { 961 | case 'opfs-async-init': { 962 | /* Receive shared state from synchronous partner */ 963 | const opt = data.args; 964 | for (const k in opt) state[k] = opt[k]; 965 | state.verbose = opt.verbose ?? 1; 966 | state.sabOPView = new Int32Array(state.sabOP); 967 | state.sabFileBufView = new Uint8Array( 968 | state.sabIO, 969 | 0, 970 | state.fileBufferSize, 971 | ); 972 | state.sabS11nView = new Uint8Array( 973 | state.sabIO, 974 | state.sabS11nOffset, 975 | state.sabS11nSize, 976 | ); 977 | Object.keys(vfsAsyncImpls).forEach((k) => { 978 | if (!Number.isFinite(state.opIds[k])) { 979 | toss('Maintenance required: missing state.opIds[', k, ']'); 980 | } 981 | }); 982 | initS11n(); 983 | metrics.reset(); 984 | log('init state', state); 985 | wPost('opfs-async-inited'); 986 | waitLoop(); 987 | break; 988 | } 989 | case 'opfs-async-restart': 990 | if (flagAsyncShutdown) { 991 | warn( 992 | 'Restarting after opfs-async-shutdown. Might or might not work.', 993 | ); 994 | flagAsyncShutdown = false; 995 | waitLoop(); 996 | } 997 | break; 998 | case 'opfs-async-metrics': 999 | metrics.dump(); 1000 | break; 1001 | } 1002 | }; 1003 | wPost('opfs-async-loaded'); 1004 | }) 1005 | .catch((e) => error('error initializing OPFS asyncer:', e)); 1006 | }; /*installAsyncProxy()*/ 1007 | if (!globalThis.SharedArrayBuffer) { 1008 | wPost( 1009 | 'opfs-unavailable', 1010 | 'Missing SharedArrayBuffer API.', 1011 | 'The server must emit the COOP/COEP response headers to enable that.', 1012 | ); 1013 | } else if (!globalThis.Atomics) { 1014 | wPost( 1015 | 'opfs-unavailable', 1016 | 'Missing Atomics API.', 1017 | 'The server must emit the COOP/COEP response headers to enable that.', 1018 | ); 1019 | } else if ( 1020 | !globalThis.FileSystemHandle || 1021 | !globalThis.FileSystemDirectoryHandle || 1022 | !globalThis.FileSystemFileHandle || 1023 | !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || 1024 | !navigator?.storage?.getDirectory 1025 | ) { 1026 | wPost('opfs-unavailable', 'Missing required OPFS APIs.'); 1027 | } else { 1028 | installAsyncProxy(self); 1029 | } 1030 | -------------------------------------------------------------------------------- /sqlite-wasm/jswasm/sqlite3-worker1-bundler-friendly.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | 2022-05-23 3 | 4 | The author disclaims copyright to this source code. In place of a 5 | legal notice, here is a blessing: 6 | 7 | * May you do good and not evil. 8 | * May you find forgiveness for yourself and forgive others. 9 | * May you share freely, never taking more than you give. 10 | 11 | *********************************************************************** 12 | 13 | This is a JS Worker file for the main sqlite3 api. It loads 14 | sqlite3.js, initializes the module, and postMessage()'s a message 15 | after the module is initialized: 16 | 17 | {type: 'sqlite3-api', result: 'worker1-ready'} 18 | 19 | This seemingly superfluous level of indirection is necessary when 20 | loading sqlite3.js via a Worker. Instantiating a worker with new 21 | Worker("sqlite.js") will not (cannot) call sqlite3InitModule() to 22 | initialize the module due to a timing/order-of-operations conflict 23 | (and that symbol is not exported in a way that a Worker loading it 24 | that way can see it). Thus JS code wanting to load the sqlite3 25 | Worker-specific API needs to pass _this_ file (or equivalent) to the 26 | Worker constructor and then listen for an event in the form shown 27 | above in order to know when the module has completed initialization. 28 | 29 | This file accepts a URL arguments to adjust how it loads sqlite3.js: 30 | 31 | - `sqlite3.dir`, if set, treats the given directory name as the 32 | directory from which `sqlite3.js` will be loaded. 33 | */ 34 | import { default as sqlite3InitModule } from './sqlite3-bundler-friendly.mjs'; 35 | sqlite3InitModule().then((sqlite3) => sqlite3.initWorker1API()); 36 | -------------------------------------------------------------------------------- /sqlite-wasm/jswasm/sqlite3-worker1-promiser-bundler-friendly.js: -------------------------------------------------------------------------------- 1 | /* 2 | 2022-08-24 3 | 4 | The author disclaims copyright to this source code. In place of a 5 | legal notice, here is a blessing: 6 | 7 | * May you do good and not evil. 8 | * May you find forgiveness for yourself and forgive others. 9 | * May you share freely, never taking more than you give. 10 | 11 | *********************************************************************** 12 | 13 | This file implements a Promise-based proxy for the sqlite3 Worker 14 | API #1. It is intended to be included either from the main thread or 15 | a Worker, but only if (A) the environment supports nested Workers 16 | and (B) it's _not_ a Worker which loads the sqlite3 WASM/JS 17 | module. This file's features will load that module and provide a 18 | slightly simpler client-side interface than the slightly-lower-level 19 | Worker API does. 20 | 21 | This script necessarily exposes one global symbol, but clients may 22 | freely `delete` that symbol after calling it. 23 | */ 24 | 'use strict'; 25 | /** 26 | * Configures an sqlite3 Worker API #1 Worker such that it can be manipulated 27 | * via a Promise-based interface and returns a factory function which returns 28 | * Promises for communicating with the worker. This proxy has an _almost_ 29 | * identical interface to the normal worker API, with any exceptions documented 30 | * below. 31 | * 32 | * It requires a configuration object with the following properties: 33 | * 34 | * - `worker` (required): a Worker instance which loads `sqlite3-worker1.js` or a 35 | * functional equivalent. Note that the promiser factory replaces the 36 | * worker.onmessage property. This config option may alternately be a 37 | * function, in which case this function re-assigns this property with the 38 | * result of calling that function, enabling delayed instantiation of a 39 | * Worker. 40 | * - `onready` (optional, but...): this callback is called with no arguments when 41 | * the worker fires its initial 'sqlite3-api'/'worker1-ready' message, which 42 | * it does when sqlite3.initWorker1API() completes its initialization. This is 43 | * the simplest way to tell the worker to kick off work at the earliest 44 | * opportunity. 45 | * - `onunhandled` (optional): a callback which gets passed the message event 46 | * object for any worker.onmessage() events which are not handled by this 47 | * proxy. Ideally that "should" never happen, as this proxy aims to handle all 48 | * known message types. 49 | * - `generateMessageId` (optional): a function which, when passed an 50 | * about-to-be-posted message object, generates a _unique_ message ID for the 51 | * message, which this API then assigns as the messageId property of the 52 | * message. It _must_ generate unique IDs on each call so that dispatching can 53 | * work. If not defined, a default generator is used (which should be 54 | * sufficient for most or all cases). 55 | * - `debug` (optional): a console.debug()-style function for logging information 56 | * about messages. 57 | * 58 | * This function returns a stateful factory function with the following 59 | * interfaces: 60 | * 61 | * - Promise function(messageType, messageArgs) 62 | * - Promise function({message object}) 63 | * 64 | * The first form expects the "type" and "args" values for a Worker message. The 65 | * second expects an object in the form {type:..., args:...} plus any other 66 | * properties the client cares to set. This function will always set the 67 | * `messageId` property on the object, even if it's already set, and will set 68 | * the `dbId` property to the current database ID if it is _not_ set in the 69 | * message object. 70 | * 71 | * The function throws on error. 72 | * 73 | * The function installs a temporary message listener, posts a message to the 74 | * configured Worker, and handles the message's response via the temporary 75 | * message listener. The then() callback of the returned Promise is passed the 76 | * `message.data` property from the resulting message, i.e. the payload from the 77 | * worker, stripped of the lower-level event state which the onmessage() handler 78 | * receives. 79 | * 80 | * Example usage: 81 | * 82 | * const config = {...}; 83 | * const sq3Promiser = sqlite3Worker1Promiser(config); 84 | * sq3Promiser('open', {filename:"/foo.db"}).then(function(msg){ 85 | * console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} 86 | * }); 87 | * sq3Promiser({type:'close'}).then((msg)=>{ 88 | * console.log("close response",msg); // => {type:'close', result: {filename:'/foo.db'}, ...} 89 | * }); 90 | * 91 | * Differences from Worker API #1: 92 | * 93 | * - Exec's {callback: STRING} option does not work via this interface (it 94 | * triggers an exception), but {callback: function} does and works exactly 95 | * like the STRING form does in the Worker: the callback is called one time 96 | * for each row of the result set, passed the same worker message format as 97 | * the worker API emits: 98 | * 99 | * {type:typeString, row:VALUE, rowNumber:1-based-#, columnNames: array} 100 | * 101 | * Where `typeString` is an internally-synthesized message type string used 102 | * temporarily for worker message dispatching. It can be ignored by all client 103 | * code except that which tests this API. The `row` property contains the row 104 | * result in the form implied by the `rowMode` option (defaulting to `'array'`). 105 | * The `rowNumber` is a 1-based integer value incremented by 1 on each call into 106 | * the callback. 107 | * 108 | * At the end of the result set, the same event is fired with (row=undefined, 109 | * rowNumber=null) to indicate that the end of the result set has been reached. 110 | * Note that the rows arrive via worker-posted messages, with all the 111 | * implications of that. 112 | * 113 | * Notable shortcomings: 114 | * 115 | * - This API was not designed with ES6 modules in mind. Neither Firefox nor 116 | * Safari support, as of March 2023, the {type:"module"} flag to the Worker 117 | * constructor, so that particular usage is not something we're going to 118 | * target for the time being: 119 | * 120 | * https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker 121 | */ 122 | globalThis.sqlite3Worker1Promiser = function callee( 123 | config = callee.defaultConfig, 124 | ) { 125 | // Inspired by: https://stackoverflow.com/a/52439530 126 | if (1 === arguments.length && 'function' === typeof arguments[0]) { 127 | const f = config; 128 | config = Object.assign(Object.create(null), callee.defaultConfig); 129 | config.onready = f; 130 | } else { 131 | config = Object.assign(Object.create(null), callee.defaultConfig, config); 132 | } 133 | const handlerMap = Object.create(null); 134 | const noop = function () {}; 135 | const err = 136 | config.onerror || noop; /* config.onerror is intentionally undocumented 137 | pending finding a less ambiguous name */ 138 | const debug = config.debug || noop; 139 | const idTypeMap = config.generateMessageId ? undefined : Object.create(null); 140 | const genMsgId = 141 | config.generateMessageId || 142 | function (msg) { 143 | return ( 144 | msg.type + '#' + (idTypeMap[msg.type] = (idTypeMap[msg.type] || 0) + 1) 145 | ); 146 | }; 147 | const toss = (...args) => { 148 | throw new Error(args.join(' ')); 149 | }; 150 | if (!config.worker) config.worker = callee.defaultConfig.worker; 151 | if ('function' === typeof config.worker) config.worker = config.worker(); 152 | let dbId; 153 | let promiserFunc; 154 | config.worker.onmessage = function (ev) { 155 | ev = ev.data; 156 | debug('worker1.onmessage', ev); 157 | let msgHandler = handlerMap[ev.messageId]; 158 | if (!msgHandler) { 159 | if (ev && 'sqlite3-api' === ev.type && 'worker1-ready' === ev.result) { 160 | /*fired one time when the Worker1 API initializes*/ 161 | if (config.onready) config.onready(promiserFunc); 162 | return; 163 | } 164 | msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; 165 | if (msgHandler && msgHandler.onrow) { 166 | msgHandler.onrow(ev); 167 | return; 168 | } 169 | if (config.onunhandled) config.onunhandled(arguments[0]); 170 | else err('sqlite3Worker1Promiser() unhandled worker message:', ev); 171 | return; 172 | } 173 | delete handlerMap[ev.messageId]; 174 | switch (ev.type) { 175 | case 'error': 176 | msgHandler.reject(ev); 177 | return; 178 | case 'open': 179 | if (!dbId) dbId = ev.dbId; 180 | break; 181 | case 'close': 182 | if (ev.dbId === dbId) dbId = undefined; 183 | break; 184 | default: 185 | break; 186 | } 187 | try { 188 | msgHandler.resolve(ev); 189 | } catch (e) { 190 | msgHandler.reject(e); 191 | } 192 | } /*worker.onmessage()*/; 193 | return (promiserFunc = function (/*(msgType, msgArgs) || (msgEnvelope)*/) { 194 | let msg; 195 | if (1 === arguments.length) { 196 | msg = arguments[0]; 197 | } else if (2 === arguments.length) { 198 | msg = Object.create(null); 199 | msg.type = arguments[0]; 200 | msg.args = arguments[1]; 201 | msg.dbId = msg.args.dbId; 202 | } else { 203 | toss('Invalid arugments for sqlite3Worker1Promiser()-created factory.'); 204 | } 205 | if (!msg.dbId && msg.type !== 'open') msg.dbId = dbId; 206 | msg.messageId = genMsgId(msg); 207 | msg.departureTime = performance.now(); 208 | const proxy = Object.create(null); 209 | proxy.message = msg; 210 | let rowCallbackId /* message handler ID for exec on-row callback proxy */; 211 | if ('exec' === msg.type && msg.args) { 212 | if ('function' === typeof msg.args.callback) { 213 | rowCallbackId = msg.messageId + ':row'; 214 | proxy.onrow = msg.args.callback; 215 | msg.args.callback = rowCallbackId; 216 | handlerMap[rowCallbackId] = proxy; 217 | } else if ('string' === typeof msg.args.callback) { 218 | toss( 219 | 'exec callback may not be a string when using the Promise interface.', 220 | ); 221 | /** 222 | * Design note: the reason for this limitation is that this API takes 223 | * over worker.onmessage() and the client has no way of adding their own 224 | * message-type handlers to it. Per-row callbacks are implemented as 225 | * short-lived message.type mappings for worker.onmessage(). 226 | * 227 | * We "could" work around this by providing a new 228 | * config.fallbackMessageHandler (or some such) which contains 229 | * a map of event type names to callbacks. Seems like overkill 230 | * for now, seeing as the client can pass callback functions 231 | * to this interface (whereas the string-form "callback" is 232 | * needed for the over-the-Worker interface). 233 | */ 234 | } 235 | } 236 | //debug("requestWork", msg); 237 | let p = new Promise(function (resolve, reject) { 238 | proxy.resolve = resolve; 239 | proxy.reject = reject; 240 | handlerMap[msg.messageId] = proxy; 241 | debug( 242 | 'Posting', 243 | msg.type, 244 | 'message to Worker dbId=' + (dbId || 'default') + ':', 245 | msg, 246 | ); 247 | config.worker.postMessage(msg); 248 | }); 249 | if (rowCallbackId) p = p.finally(() => delete handlerMap[rowCallbackId]); 250 | return p; 251 | }); 252 | } /*sqlite3Worker1Promiser()*/; 253 | globalThis.sqlite3Worker1Promiser.defaultConfig = { 254 | worker: function () { 255 | return new Worker( 256 | new URL('sqlite3-worker1-bundler-friendly.mjs', import.meta.url), 257 | { 258 | type: 'module', 259 | }, 260 | ); 261 | }, 262 | onerror: (...args) => console.error('worker1 promiser error', ...args), 263 | }; 264 | -------------------------------------------------------------------------------- /sqlite-wasm/jswasm/sqlite3-worker1-promiser.js: -------------------------------------------------------------------------------- 1 | /* 2 | 2022-08-24 3 | 4 | The author disclaims copyright to this source code. In place of a 5 | legal notice, here is a blessing: 6 | 7 | * May you do good and not evil. 8 | * May you find forgiveness for yourself and forgive others. 9 | * May you share freely, never taking more than you give. 10 | 11 | *********************************************************************** 12 | 13 | This file implements a Promise-based proxy for the sqlite3 Worker 14 | API #1. It is intended to be included either from the main thread or 15 | a Worker, but only if (A) the environment supports nested Workers 16 | and (B) it's _not_ a Worker which loads the sqlite3 WASM/JS 17 | module. This file's features will load that module and provide a 18 | slightly simpler client-side interface than the slightly-lower-level 19 | Worker API does. 20 | 21 | This script necessarily exposes one global symbol, but clients may 22 | freely `delete` that symbol after calling it. 23 | */ 24 | 'use strict'; 25 | /** 26 | * Configures an sqlite3 Worker API #1 Worker such that it can be manipulated 27 | * via a Promise-based interface and returns a factory function which returns 28 | * Promises for communicating with the worker. This proxy has an _almost_ 29 | * identical interface to the normal worker API, with any exceptions documented 30 | * below. 31 | * 32 | * It requires a configuration object with the following properties: 33 | * 34 | * - `worker` (required): a Worker instance which loads `sqlite3-worker1.js` or a 35 | * functional equivalent. Note that the promiser factory replaces the 36 | * worker.onmessage property. This config option may alternately be a 37 | * function, in which case this function re-assigns this property with the 38 | * result of calling that function, enabling delayed instantiation of a 39 | * Worker. 40 | * - `onready` (optional, but...): this callback is called with no arguments when 41 | * the worker fires its initial 'sqlite3-api'/'worker1-ready' message, which 42 | * it does when sqlite3.initWorker1API() completes its initialization. This is 43 | * the simplest way to tell the worker to kick off work at the earliest 44 | * opportunity. 45 | * - `onunhandled` (optional): a callback which gets passed the message event 46 | * object for any worker.onmessage() events which are not handled by this 47 | * proxy. Ideally that "should" never happen, as this proxy aims to handle all 48 | * known message types. 49 | * - `generateMessageId` (optional): a function which, when passed an 50 | * about-to-be-posted message object, generates a _unique_ message ID for the 51 | * message, which this API then assigns as the messageId property of the 52 | * message. It _must_ generate unique IDs on each call so that dispatching can 53 | * work. If not defined, a default generator is used (which should be 54 | * sufficient for most or all cases). 55 | * - `debug` (optional): a console.debug()-style function for logging information 56 | * about messages. 57 | * 58 | * This function returns a stateful factory function with the following 59 | * interfaces: 60 | * 61 | * - Promise function(messageType, messageArgs) 62 | * - Promise function({message object}) 63 | * 64 | * The first form expects the "type" and "args" values for a Worker message. The 65 | * second expects an object in the form {type:..., args:...} plus any other 66 | * properties the client cares to set. This function will always set the 67 | * `messageId` property on the object, even if it's already set, and will set 68 | * the `dbId` property to the current database ID if it is _not_ set in the 69 | * message object. 70 | * 71 | * The function throws on error. 72 | * 73 | * The function installs a temporary message listener, posts a message to the 74 | * configured Worker, and handles the message's response via the temporary 75 | * message listener. The then() callback of the returned Promise is passed the 76 | * `message.data` property from the resulting message, i.e. the payload from the 77 | * worker, stripped of the lower-level event state which the onmessage() handler 78 | * receives. 79 | * 80 | * Example usage: 81 | * 82 | * const config = {...}; 83 | * const sq3Promiser = sqlite3Worker1Promiser(config); 84 | * sq3Promiser('open', {filename:"/foo.db"}).then(function(msg){ 85 | * console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} 86 | * }); 87 | * sq3Promiser({type:'close'}).then((msg)=>{ 88 | * console.log("close response",msg); // => {type:'close', result: {filename:'/foo.db'}, ...} 89 | * }); 90 | * 91 | * Differences from Worker API #1: 92 | * 93 | * - Exec's {callback: STRING} option does not work via this interface (it 94 | * triggers an exception), but {callback: function} does and works exactly 95 | * like the STRING form does in the Worker: the callback is called one time 96 | * for each row of the result set, passed the same worker message format as 97 | * the worker API emits: 98 | * 99 | * {type:typeString, row:VALUE, rowNumber:1-based-#, columnNames: array} 100 | * 101 | * Where `typeString` is an internally-synthesized message type string used 102 | * temporarily for worker message dispatching. It can be ignored by all client 103 | * code except that which tests this API. The `row` property contains the row 104 | * result in the form implied by the `rowMode` option (defaulting to `'array'`). 105 | * The `rowNumber` is a 1-based integer value incremented by 1 on each call into 106 | * the callback. 107 | * 108 | * At the end of the result set, the same event is fired with (row=undefined, 109 | * rowNumber=null) to indicate that the end of the result set has been reached. 110 | * Note that the rows arrive via worker-posted messages, with all the 111 | * implications of that. 112 | * 113 | * Notable shortcomings: 114 | * 115 | * - This API was not designed with ES6 modules in mind. Neither Firefox nor 116 | * Safari support, as of March 2023, the {type:"module"} flag to the Worker 117 | * constructor, so that particular usage is not something we're going to 118 | * target for the time being: 119 | * 120 | * https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker 121 | */ 122 | globalThis.sqlite3Worker1Promiser = function callee( 123 | config = callee.defaultConfig, 124 | ) { 125 | // Inspired by: https://stackoverflow.com/a/52439530 126 | if (1 === arguments.length && 'function' === typeof arguments[0]) { 127 | const f = config; 128 | config = Object.assign(Object.create(null), callee.defaultConfig); 129 | config.onready = f; 130 | } else { 131 | config = Object.assign(Object.create(null), callee.defaultConfig, config); 132 | } 133 | const handlerMap = Object.create(null); 134 | const noop = function () {}; 135 | const err = 136 | config.onerror || noop; /* config.onerror is intentionally undocumented 137 | pending finding a less ambiguous name */ 138 | const debug = config.debug || noop; 139 | const idTypeMap = config.generateMessageId ? undefined : Object.create(null); 140 | const genMsgId = 141 | config.generateMessageId || 142 | function (msg) { 143 | return ( 144 | msg.type + '#' + (idTypeMap[msg.type] = (idTypeMap[msg.type] || 0) + 1) 145 | ); 146 | }; 147 | const toss = (...args) => { 148 | throw new Error(args.join(' ')); 149 | }; 150 | if (!config.worker) config.worker = callee.defaultConfig.worker; 151 | if ('function' === typeof config.worker) config.worker = config.worker(); 152 | let dbId; 153 | let promiserFunc; 154 | config.worker.onmessage = function (ev) { 155 | ev = ev.data; 156 | debug('worker1.onmessage', ev); 157 | let msgHandler = handlerMap[ev.messageId]; 158 | if (!msgHandler) { 159 | if (ev && 'sqlite3-api' === ev.type && 'worker1-ready' === ev.result) { 160 | /*fired one time when the Worker1 API initializes*/ 161 | if (config.onready) config.onready(promiserFunc); 162 | return; 163 | } 164 | msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; 165 | if (msgHandler && msgHandler.onrow) { 166 | msgHandler.onrow(ev); 167 | return; 168 | } 169 | if (config.onunhandled) config.onunhandled(arguments[0]); 170 | else err('sqlite3Worker1Promiser() unhandled worker message:', ev); 171 | return; 172 | } 173 | delete handlerMap[ev.messageId]; 174 | switch (ev.type) { 175 | case 'error': 176 | msgHandler.reject(ev); 177 | return; 178 | case 'open': 179 | if (!dbId) dbId = ev.dbId; 180 | break; 181 | case 'close': 182 | if (ev.dbId === dbId) dbId = undefined; 183 | break; 184 | default: 185 | break; 186 | } 187 | try { 188 | msgHandler.resolve(ev); 189 | } catch (e) { 190 | msgHandler.reject(e); 191 | } 192 | } /*worker.onmessage()*/; 193 | return (promiserFunc = function (/*(msgType, msgArgs) || (msgEnvelope)*/) { 194 | let msg; 195 | if (1 === arguments.length) { 196 | msg = arguments[0]; 197 | } else if (2 === arguments.length) { 198 | msg = Object.create(null); 199 | msg.type = arguments[0]; 200 | msg.args = arguments[1]; 201 | msg.dbId = msg.args.dbId; 202 | } else { 203 | toss('Invalid arugments for sqlite3Worker1Promiser()-created factory.'); 204 | } 205 | if (!msg.dbId && msg.type !== 'open') msg.dbId = dbId; 206 | msg.messageId = genMsgId(msg); 207 | msg.departureTime = performance.now(); 208 | const proxy = Object.create(null); 209 | proxy.message = msg; 210 | let rowCallbackId /* message handler ID for exec on-row callback proxy */; 211 | if ('exec' === msg.type && msg.args) { 212 | if ('function' === typeof msg.args.callback) { 213 | rowCallbackId = msg.messageId + ':row'; 214 | proxy.onrow = msg.args.callback; 215 | msg.args.callback = rowCallbackId; 216 | handlerMap[rowCallbackId] = proxy; 217 | } else if ('string' === typeof msg.args.callback) { 218 | toss( 219 | 'exec callback may not be a string when using the Promise interface.', 220 | ); 221 | /** 222 | * Design note: the reason for this limitation is that this API takes 223 | * over worker.onmessage() and the client has no way of adding their own 224 | * message-type handlers to it. Per-row callbacks are implemented as 225 | * short-lived message.type mappings for worker.onmessage(). 226 | * 227 | * We "could" work around this by providing a new 228 | * config.fallbackMessageHandler (or some such) which contains 229 | * a map of event type names to callbacks. Seems like overkill 230 | * for now, seeing as the client can pass callback functions 231 | * to this interface (whereas the string-form "callback" is 232 | * needed for the over-the-Worker interface). 233 | */ 234 | } 235 | } 236 | //debug("requestWork", msg); 237 | let p = new Promise(function (resolve, reject) { 238 | proxy.resolve = resolve; 239 | proxy.reject = reject; 240 | handlerMap[msg.messageId] = proxy; 241 | debug( 242 | 'Posting', 243 | msg.type, 244 | 'message to Worker dbId=' + (dbId || 'default') + ':', 245 | msg, 246 | ); 247 | config.worker.postMessage(msg); 248 | }); 249 | if (rowCallbackId) p = p.finally(() => delete handlerMap[rowCallbackId]); 250 | return p; 251 | }); 252 | } /*sqlite3Worker1Promiser()*/; 253 | globalThis.sqlite3Worker1Promiser.defaultConfig = { 254 | worker: function () { 255 | let theJs = 'sqlite3-worker1.js'; 256 | if (this.currentScript) { 257 | const src = this.currentScript.src.split('/'); 258 | src.pop(); 259 | theJs = src.join('/') + '/' + theJs; 260 | //sqlite3.config.warn("promiser currentScript, theJs =",this.currentScript,theJs); 261 | } else if (globalThis.location) { 262 | //sqlite3.config.warn("promiser globalThis.location =",globalThis.location); 263 | const urlParams = new URL(globalThis.location.href).searchParams; 264 | if (urlParams.has('sqlite3.dir')) { 265 | theJs = urlParams.get('sqlite3.dir') + '/' + theJs; 266 | } 267 | } 268 | return new Worker(theJs + globalThis.location.search); 269 | }.bind({ 270 | currentScript: globalThis?.document?.currentScript, 271 | }), 272 | onerror: (...args) => console.error('worker1 promiser error', ...args), 273 | }; 274 | -------------------------------------------------------------------------------- /sqlite-wasm/jswasm/sqlite3-worker1.js: -------------------------------------------------------------------------------- 1 | /* 2 | 2022-05-23 3 | 4 | The author disclaims copyright to this source code. In place of a 5 | legal notice, here is a blessing: 6 | 7 | * May you do good and not evil. 8 | * May you find forgiveness for yourself and forgive others. 9 | * May you share freely, never taking more than you give. 10 | 11 | *********************************************************************** 12 | 13 | This is a JS Worker file for the main sqlite3 api. It loads 14 | sqlite3.js, initializes the module, and postMessage()'s a message 15 | after the module is initialized: 16 | 17 | {type: 'sqlite3-api', result: 'worker1-ready'} 18 | 19 | This seemingly superfluous level of indirection is necessary when 20 | loading sqlite3.js via a Worker. Instantiating a worker with new 21 | Worker("sqlite.js") will not (cannot) call sqlite3InitModule() to 22 | initialize the module due to a timing/order-of-operations conflict 23 | (and that symbol is not exported in a way that a Worker loading it 24 | that way can see it). Thus JS code wanting to load the sqlite3 25 | Worker-specific API needs to pass _this_ file (or equivalent) to the 26 | Worker constructor and then listen for an event in the form shown 27 | above in order to know when the module has completed initialization. 28 | 29 | This file accepts a URL arguments to adjust how it loads sqlite3.js: 30 | 31 | - `sqlite3.dir`, if set, treats the given directory name as the 32 | directory from which `sqlite3.js` will be loaded. 33 | */ 34 | 'use strict'; 35 | { 36 | const urlParams = globalThis.location 37 | ? new URL(globalThis.location.href).searchParams 38 | : new URLSearchParams(); 39 | let theJs = 'sqlite3.js'; 40 | if (urlParams.has('sqlite3.dir')) { 41 | theJs = urlParams.get('sqlite3.dir') + '/' + theJs; 42 | } 43 | //console.warn("worker1 theJs =",theJs); 44 | importScripts(theJs); 45 | } 46 | sqlite3InitModule().then((sqlite3) => sqlite3.initWorker1API()); 47 | -------------------------------------------------------------------------------- /sqlite-wasm/jswasm/sqlite3.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livestorejs/sqlite-wasm/5a83497e5c36289ac4475ceacf0e72ea6af493b7/sqlite-wasm/jswasm/sqlite3.wasm --------------------------------------------------------------------------------