From f48bc80739ad9871e2f2664d745b72a2e81e14e5 Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Thu, 9 Apr 2026 17:16:02 +0200 Subject: [PATCH 1/3] chore: bumped version and deps --- package-lock.json | 1014 ++++++++++++++++++++++++++++++--------------- package.json | 22 +- 2 files changed, 696 insertions(+), 340 deletions(-) diff --git a/package-lock.json b/package-lock.json index fdb30f6..6640f21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,34 +9,34 @@ "version": "3.51.2-build8", "license": "Apache-2.0", "devDependencies": { - "@types/node": "^25.5.0", - "@typescript/native-preview": "^7.0.0-dev.20260315.1", - "@vitest/browser": "^4.1.0", - "@vitest/browser-playwright": "^4.1.0", - "happy-dom": "^20.8.4", + "@types/node": "^25.5.2", + "@typescript/native-preview": "^7.0.0-dev.20260409.1", + "@vitest/browser": "^4.1.4", + "@vitest/browser-playwright": "^4.1.4", + "happy-dom": "20.8.9", "http-server": "github:vapier/http-server", - "lefthook": "2.1.4", - "playwright": "^1.58.2", + "lefthook": "2.1.5", + "playwright": "^1.59.1", "prettier": "^3.8.1", "prettier-plugin-jsdoc": "^1.8.0", "publint": "^0.3.18", - "tsdown": "^0.21.3", - "typescript": "^5.9.3", - "vitest": "^4.1.0" + "tsdown": "^0.21.7", + "typescript": "^6.0.2", + "vitest": "^4.1.4" }, "engines": { "node": ">=22" } }, "node_modules/@babel/generator": { - "version": "8.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0-rc.2.tgz", - "integrity": "sha512-oCQ1IKPwkzCeJzAPb7Fv8rQ9k5+1sG8mf2uoHiMInPYvkRfrDJxbTIbH51U+jstlkghus0vAi3EBvkfvEsYNLQ==", + "version": "8.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0-rc.3.tgz", + "integrity": "sha512-em37/13/nR320G4jab/nIIHZgc2Wz2y/D39lxnTyxB4/D/omPQncl/lSdlnJY1OhQcRGugTSIF2l/69o31C9dA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^8.0.0-rc.2", - "@babel/types": "^8.0.0-rc.2", + "@babel/parser": "^8.0.0-rc.3", + "@babel/types": "^8.0.0-rc.3", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "@types/jsesc": "^2.5.0", @@ -47,9 +47,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "8.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0-rc.2.tgz", - "integrity": "sha512-noLx87RwlBEMrTzncWd/FvTxoJ9+ycHNg0n8yyYydIoDsLZuxknKgWRJUqcrVkNrJ74uGyhWQzQaS3q8xfGAhQ==", + "version": "8.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0-rc.3.tgz", + "integrity": "sha512-AmwWFx1m8G/a5cXkxLxTiWl+YEoWuoFLUCwqMlNuWO1tqAYITQAbCRPUkyBHv1VOFgfjVOqEj6L3u15J5ZCzTA==", "dev": true, "license": "MIT", "engines": { @@ -57,9 +57,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "8.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-rc.2.tgz", - "integrity": "sha512-xExUBkuXWJjVuIbO7z6q7/BA9bgfJDEhVL0ggrggLMbg0IzCUWGT1hZGE8qUH7Il7/RD/a6cZ3AAFrrlp1LF/A==", + "version": "8.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-rc.3.tgz", + "integrity": "sha512-8AWCJ2VJJyDFlGBep5GpaaQ9AAaE/FjAcrqI7jyssYhtL7WGV0DOKpJsQqM037xDbpRLHXsY8TwU7zDma7coOw==", "dev": true, "license": "MIT", "engines": { @@ -67,13 +67,13 @@ } }, "node_modules/@babel/parser": { - "version": "8.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0-rc.2.tgz", - "integrity": "sha512-29AhEtcq4x8Dp3T72qvUMZHx0OMXCj4Jy/TEReQa+KWLln524Cj1fWb3QFi0l/xSpptQBR6y9RNEXuxpFvwiUQ==", + "version": "8.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0-rc.3.tgz", + "integrity": "sha512-B20dvP3MfNc/XS5KKCHy/oyWl5IA6Cn9YjXRdDlCjNmUFrjvLXMNUfQq/QUy9fnG2gYkKKcrto2YaF9B32ToOQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^8.0.0-rc.2" + "@babel/types": "^8.0.0-rc.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -83,14 +83,14 @@ } }, "node_modules/@babel/types": { - "version": "8.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0-rc.2.tgz", - "integrity": "sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw==", + "version": "8.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0-rc.3.tgz", + "integrity": "sha512-mOm5ZrYmphGfqVWoH5YYMTITb3cDXsFgmvFlvkvWDMsR9X8RFnt7a0Wb6yNIdoFsiMO9WjYLq+U/FMtqIYAF8Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^8.0.0-rc.2", - "@babel/helper-validator-identifier": "^8.0.0-rc.2" + "@babel/helper-string-parser": "^8.0.0-rc.3", + "@babel/helper-validator-identifier": "^8.0.0-rc.3" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -104,21 +104,21 @@ "license": "MIT" }, "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.1.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "dev": true, "license": "MIT", "optional": true, @@ -127,9 +127,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, @@ -177,36 +177,28 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", + "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@oxc-project/runtime": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", - "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@oxc-project/types": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", - "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", "dev": true, "license": "MIT", "funding": { @@ -247,9 +239,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", "cpu": [ "arm64" ], @@ -264,9 +256,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", "cpu": [ "arm64" ], @@ -281,9 +273,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", "cpu": [ "x64" ], @@ -298,9 +290,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", "cpu": [ "x64" ], @@ -315,9 +307,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", - "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", "cpu": [ "arm" ], @@ -332,13 +324,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -349,13 +344,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -366,13 +364,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -383,13 +384,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -400,13 +404,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -417,13 +424,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -434,9 +444,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", "cpu": [ "arm64" ], @@ -451,9 +461,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", - "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", "cpu": [ "wasm32" ], @@ -468,9 +478,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", "cpu": [ "arm64" ], @@ -485,9 +495,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", "cpu": [ "x64" ], @@ -502,9 +512,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", - "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", "dev": true, "license": "MIT" }, @@ -586,9 +596,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", "dev": true, "license": "MIT", "dependencies": { @@ -620,28 +630,28 @@ } }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-t+st0mCz4HpvODTTlj2XxIQtiNT7L7lxP91790MOfA0xTRgwu7ERYV7WB1SbXRyrFDIwuO1bZqT0E0P4qcL4RQ==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-CV1HEMGo1xCySwUJbCQOF+mmrTue8KTJ1Od2kKWhcbOpu8fPBfaqIpbAM6tGLcNEykEjMMTYHc/VTLbMgxdScQ==", "dev": true, "license": "Apache-2.0", "bin": { "tsgo": "bin/tsgo.js" }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260315.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260315.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20260315.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260315.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20260315.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260315.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20260315.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260409.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260409.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20260409.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260409.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20260409.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260409.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20260409.1" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-UhRPJWUZMHO1Xuurjr28gR98+nwD+QBJiUWzTLLrvhkGEOA8IG9Q8hqzJ07AQb/V251F/MsEjOSEnHmgGNT6Dg==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-GcRRnaoeZVrbC47woQ/2t3vPoQcTSjsWPEAQGtwNSdw7Z9TKxG4ES22ghJIQXd3ncTRCMJ+XELnnuqxVutkJ9w==", "cpu": [ "arm64" ], @@ -653,9 +663,9 @@ ] }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-7a2P4KyJQF83Zj+3Vi/VCV9I/0lC+2QRgD4JEIh1H0FliRDZILUIbiAqWaksHHl4pBMtlRZr+1hjad5vPUsQmA==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-7s8DXAa0Xpu/8PEjYIc4I36Ju7eVpoz9k3E+3WQdOF8pIPWYohiOj+zi68m9XYQck+rnkjUFo26ThVKqVetoMA==", "cpu": [ "x64" ], @@ -667,9 +677,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-n2MhBeDAmdbp6DOtz+I2JeGpNzepzvXxPEDpRE+syAT5mTXstwTk+9w4rF2SLt1YgLuPmon7iZhkUyPTd0dD7A==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-fOa07JBUXQpEPq+024g346inYZ2xp63ELuoRq6J0jwDWQ/ftCCuvdQNMncwFhsm1qlMdKT3S68NrnSxX16hiaw==", "cpu": [ "arm" ], @@ -681,9 +691,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-DwKY8zVqsQx0+golSKopbzZVIVfnsUVVt7s6Wg5kiAYrFy32TNKqANfbiWLBe5PGSpYu9Mx1XWLpsvi6/58BWw==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-cGTzTUqRGlIDwdtkDy6qTrvrqpe27W4CdgnFn0FpxpiWnaIi3wqjlzQ1grtqrqainw/yuPy5hn/I86sQgN6nvA==", "cpu": [ "arm64" ], @@ -695,9 +705,9 @@ ] }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-5kRxtdfqF9X1q/vIg7myq7D0MmF3GywZXS3mmZq6TQEFiu5IClHPaQCuCQqYdtHmHtZllHZz0VaEtnvV9Vamrw==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-lQrbc/BJKBxQrR1ttBDU5sYY1Hb2moFQgHL20T6nbapNqGpK4pzy64p+NK39O93D4omiCSk04pkchBCVrMPSAg==", "cpu": [ "x64" ], @@ -709,9 +719,9 @@ ] }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-u0ixXTG/97k2eVRQfwafGckHuK60st5ADYn23KQvZJxeUZ47XWIjzCL1JLcoiRexEAwsEerireyy32l4LxA9BQ==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-kmCafMo1xZlYx+9WnfpeZJ2tnB/CcJdR8QPX7j9vqcpe51D7b7Intmr921dD48KGpVh5YgjQ1MEFE5mjGqGMaA==", "cpu": [ "arm64" ], @@ -723,9 +733,9 @@ ] }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20260315.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260315.1.tgz", - "integrity": "sha512-iSnVmAZgogCup/5SOF2h+Hwdywa4cGNIw9z8jpTVJof56w5GR9Hx3vN9UszqszjGxPNobYERcOk8QhGZO8uf5g==", + "version": "7.0.0-dev.20260409.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260409.1.tgz", + "integrity": "sha512-WRd+JpQipTsE15QgYr3w7J0f1NKvGcq2QEgmcq8hB0WZA1X2WhQopNu+MpPQ3tdDD42VjMhm8ZoB8HpuOoXK5w==", "cpu": [ "x64" ], @@ -737,45 +747,45 @@ ] }, "node_modules/@vitest/browser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.0.tgz", - "integrity": "sha512-tG/iOrgbiHQks0ew7CdelUyNEHkv8NLrt+CqdTivIuoSnXvO7scWMn4Kqo78/UGY1NJ6Hv+vp8BvRnED/bjFdQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.4.tgz", + "integrity": "sha512-TrNaY/yVOwxtrxNsDUC/wQ56xSwplpytTeRAqF/197xV/ZddxxulBsxR6TrhVMyniJmp9in8d5u0AcDaNRY30w==", "dev": true, "license": "MIT", "dependencies": { "@blazediff/core": "1.9.1", - "@vitest/mocker": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/mocker": "4.1.4", + "@vitest/utils": "4.1.4", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", - "tinyrainbow": "^3.0.3", + "tinyrainbow": "^3.1.0", "ws": "^8.19.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.0" + "vitest": "4.1.4" } }, "node_modules/@vitest/browser-playwright": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.0.tgz", - "integrity": "sha512-2RU7pZELY9/aVMLmABNy1HeZ4FX23FXGY1jRuHLHgWa2zaAE49aNW2GLzebW+BmbTZIKKyFF1QXvk7DEWViUCQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.4.tgz", + "integrity": "sha512-q3PchVhZINX23Pv+RERgAtDlp6wzVkID/smOPnZ5YGWpeWUe3jMNYppeVh15j4il3G7JIJty1d1Kicpm0HSMig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/browser": "4.1.0", - "@vitest/mocker": "4.1.0", - "tinyrainbow": "^3.0.3" + "@vitest/browser": "4.1.4", + "@vitest/mocker": "4.1.4", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "playwright": "*", - "vitest": "4.1.0" + "vitest": "4.1.4" }, "peerDependenciesMeta": { "playwright": { @@ -784,31 +794,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", - "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz", + "integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/spy": "4.1.4", + "@vitest/utils": "4.1.4", "chai": "^6.2.2", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", - "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz", + "integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.0", + "@vitest/spy": "4.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -817,7 +827,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -829,26 +839,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", - "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", + "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", - "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz", + "integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.0", + "@vitest/utils": "4.1.4", "pathe": "^2.0.3" }, "funding": { @@ -856,14 +866,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", - "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz", + "integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/pretty-format": "4.1.4", + "@vitest/utils": "4.1.4", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -872,9 +882,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", - "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", + "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", "dev": true, "license": "MIT", "funding": { @@ -882,15 +892,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", + "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", + "@vitest/pretty-format": "4.1.4", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1401,9 +1411,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1427,9 +1437,9 @@ } }, "node_modules/happy-dom": { - "version": "20.8.4", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.8.4.tgz", - "integrity": "sha512-GKhjq4OQCYB4VLFBzv8mmccUadwlAusOZOI7hC1D9xDIT5HhzkJK17c4el2f6R6C715P9xB4uiMxeKUa2nHMwQ==", + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.8.9.tgz", + "integrity": "sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA==", "dev": true, "license": "MIT", "dependencies": { @@ -1489,9 +1499,9 @@ } }, "node_modules/hookable": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz", - "integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.0.tgz", + "integrity": "sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==", "dev": true, "license": "MIT" }, @@ -1587,9 +1597,9 @@ } }, "node_modules/lefthook": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-2.1.4.tgz", - "integrity": "sha512-JNfJ5gAn0KADvJ1I6/xMcx70+/6TL6U9gqGkKvPw5RNMfatC7jIg0Evl97HN846xmfz959BV70l8r3QsBJk30w==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-2.1.5.tgz", + "integrity": "sha512-yB9IFWurFllusbPZqvG0EavTmpNXPya2MuO7Li7YT78xAj3uCQ3AgmW9TVUbTTsSMhsegbiAMRpwfEk2TP1P0A==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1597,22 +1607,22 @@ "lefthook": "bin/index.js" }, "optionalDependencies": { - "lefthook-darwin-arm64": "2.1.4", - "lefthook-darwin-x64": "2.1.4", - "lefthook-freebsd-arm64": "2.1.4", - "lefthook-freebsd-x64": "2.1.4", - "lefthook-linux-arm64": "2.1.4", - "lefthook-linux-x64": "2.1.4", - "lefthook-openbsd-arm64": "2.1.4", - "lefthook-openbsd-x64": "2.1.4", - "lefthook-windows-arm64": "2.1.4", - "lefthook-windows-x64": "2.1.4" + "lefthook-darwin-arm64": "2.1.5", + "lefthook-darwin-x64": "2.1.5", + "lefthook-freebsd-arm64": "2.1.5", + "lefthook-freebsd-x64": "2.1.5", + "lefthook-linux-arm64": "2.1.5", + "lefthook-linux-x64": "2.1.5", + "lefthook-openbsd-arm64": "2.1.5", + "lefthook-openbsd-x64": "2.1.5", + "lefthook-windows-arm64": "2.1.5", + "lefthook-windows-x64": "2.1.5" } }, "node_modules/lefthook-darwin-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-2.1.4.tgz", - "integrity": "sha512-BUAAE9+rUrjr39a+wH/1zHmGrDdwUQ2Yq/z6BQbM/yUb9qtXBRcQ5eOXxApqWW177VhGBpX31aqIlfAZ5Q7wzw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-2.1.5.tgz", + "integrity": "sha512-VITTaw8PxxyE26gkZ8UcwIa5ZrWnKNRGLeeSrqri40cQdXvLTEoMq2tjjw7eiL9UcB0waRReDdzydevy9GOPUQ==", "cpu": [ "arm64" ], @@ -1624,9 +1634,9 @@ ] }, "node_modules/lefthook-darwin-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-2.1.4.tgz", - "integrity": "sha512-K1ncIMEe84fe+ss1hQNO7rIvqiKy2TJvTFpkypvqFodT7mJXZn7GLKYTIXdIuyPAYthRa9DwFnx5uMoHwD2F1Q==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-2.1.5.tgz", + "integrity": "sha512-AvtjYiW0BSGHBGrdvL313seUymrW9FxI+6JJwJ+ZSaa2sH81etrTB0wAwlH1L9VfFwK9+gWvatZBvLfF3L4fPw==", "cpu": [ "x64" ], @@ -1638,9 +1648,9 @@ ] }, "node_modules/lefthook-freebsd-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-2.1.4.tgz", - "integrity": "sha512-PVUhjOhVN71YaYsVdQyNbFZ4a2jFB2Tg5hKrrn9kaWpx64aLz/XivLjwr8sEuTaP1GRlEWBpW6Bhrcsyo39qFw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-2.1.5.tgz", + "integrity": "sha512-mXjJwe8jKGWGiBYUxfQY1ab3Nn5NhafqT9q3KJz8m5joGGQj4JD0cbWxF1nVBLBWsDGbWZRZunTCMGcIScT2bQ==", "cpu": [ "arm64" ], @@ -1652,9 +1662,9 @@ ] }, "node_modules/lefthook-freebsd-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-2.1.4.tgz", - "integrity": "sha512-ZWV9o/LeyWNEBoVO+BhLqxH3rGTba05nkm5NvMjEFSj7LbUNUDbQmupZwtHl1OMGJO66eZP0CalzRfUH6GhBxQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-2.1.5.tgz", + "integrity": "sha512-exD69dCjc1K45BxatDPGoH4NmEvgLKPm4kJLOWn1fTeHRKZwWiFPwnjknEoG2OemlCDHmCU++5X40kMEG0WBlA==", "cpu": [ "x64" ], @@ -1666,9 +1676,9 @@ ] }, "node_modules/lefthook-linux-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-2.1.4.tgz", - "integrity": "sha512-iWN0pGnTjrIvNIcSI1vQBJXUbybTqJ5CLMniPA0olabMXQfPDrdMKVQe+mgdwHK+E3/Y0H0ZNL3lnOj6Sk6szA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-2.1.5.tgz", + "integrity": "sha512-57TDKC5ewWpsCLZQKIJMHumFEObYKVundmPpiWhX491hINRZYYOL/26yrnVnNcidThRzTiTC+HLcuplLcaXtbA==", "cpu": [ "arm64" ], @@ -1680,9 +1690,9 @@ ] }, "node_modules/lefthook-linux-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-2.1.4.tgz", - "integrity": "sha512-96bTBE/JdYgqWYAJDh+/e/0MaxJ25XTOAk7iy/fKoZ1ugf6S0W9bEFbnCFNooXOcxNVTan5xWKfcjJmPIKtsJA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-2.1.5.tgz", + "integrity": "sha512-bqK3LrAB5l5YaCaoHk6qRWlITrGWzP4FbwRxA31elbxjd0wgNWZ2Sn3zEfSEcxz442g7/PPkEwqqsTx0kSFzpg==", "cpu": [ "x64" ], @@ -1694,9 +1704,9 @@ ] }, "node_modules/lefthook-openbsd-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-2.1.4.tgz", - "integrity": "sha512-oYUoK6AIJNEr9lUSpIMj6g7sWzotvtc3ryw7yoOyQM6uqmEduw73URV/qGoUcm4nqqmR93ZalZwR2r3Gd61zvw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-2.1.5.tgz", + "integrity": "sha512-5aSwK7vV3A6t0w9PnxCMiVjQlcvopBP50BtmnnLnNJyAYHnFbZ0Baq5M0WkE9IsUkWSux0fe6fd0jDkuG711MA==", "cpu": [ "arm64" ], @@ -1708,9 +1718,9 @@ ] }, "node_modules/lefthook-openbsd-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-2.1.4.tgz", - "integrity": "sha512-i/Dv9Jcm68y9cggr1PhyUhOabBGP9+hzQPoiyOhKks7y9qrJl79A8XfG6LHekSuYc2VpiSu5wdnnrE1cj2nfTg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-2.1.5.tgz", + "integrity": "sha512-Y+pPdDuENJ8qWnUgL02xxhpjblc0WnwXvWGfqnl3WZrAgHzQpwx3G6469RID/wlNVdHYAlw3a8UkFSMYsTzXvA==", "cpu": [ "x64" ], @@ -1722,9 +1732,9 @@ ] }, "node_modules/lefthook-windows-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-2.1.4.tgz", - "integrity": "sha512-hSww7z+QX4YMnw2lK7DMrs3+w7NtxksuMKOkCKGyxUAC/0m1LAICo0ZbtdDtZ7agxRQQQ/SEbzFRhU5ysNcbjA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-2.1.5.tgz", + "integrity": "sha512-2PlcFBjTzJaMufw0c28kfhB/0zmaRCU0TRPPsil/HU2YNOExod4upPGLk9qjgsOmb2YVWFz6zq6u7+D1yqmzTQ==", "cpu": [ "arm64" ], @@ -1736,9 +1746,9 @@ ] }, "node_modules/lefthook-windows-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-2.1.4.tgz", - "integrity": "sha512-eE68LwnogxwcPgGsbVGPGxmghyMGmU9SdGwcc+uhGnUxPz1jL89oECMWJNc36zjVK24umNeDAzB5KA3lw1MuWw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-2.1.5.tgz", + "integrity": "sha512-yiAh8qxml6uqy10jDxOdN9fOQpyLxBFY1fgCEAhn7sVJYmJKRhjqSBwZX6LG5MQjzr29KStrIdw7TR3lf3rT7Q==", "cpu": [ "x64" ], @@ -1892,6 +1902,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1913,6 +1926,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1934,6 +1950,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1955,6 +1974,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2689,9 +2711,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -2702,13 +2724,13 @@ } }, "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.58.2" + "playwright-core": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -2721,9 +2743,9 @@ } }, "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2758,9 +2780,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", "dev": true, "funding": [ { @@ -2892,14 +2914,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", - "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.115.0", - "@rolldown/pluginutils": "1.0.0-rc.9" + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" @@ -2908,39 +2930,40 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.9", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", - "@rolldown/binding-darwin-x64": "1.0.0-rc.9", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" } }, "node_modules/rolldown-plugin-dts": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.22.5.tgz", - "integrity": "sha512-M/HXfM4cboo+jONx9Z0X+CUf3B5tCi7ni+kR5fUW50Fp9AlZk0oVLesibGWgCXDKFp5lpgQ9yhKoImUFjl3VZw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.23.2.tgz", + "integrity": "sha512-PbSqLawLgZBGcOGT3yqWBGn4cX+wh2nt5FuBGdcMHyOhoukmjbhYAl8NT9sE4U38Cm9tqLOIQeOrvzeayM0DLQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/generator": "8.0.0-rc.2", - "@babel/helper-validator-identifier": "8.0.0-rc.2", - "@babel/parser": "8.0.0-rc.2", - "@babel/types": "8.0.0-rc.2", + "@babel/generator": "8.0.0-rc.3", + "@babel/helper-validator-identifier": "8.0.0-rc.3", + "@babel/parser": "8.0.0-rc.3", + "@babel/types": "8.0.0-rc.3", "ast-kit": "^3.0.0-beta.1", "birpc": "^4.0.0", "dts-resolver": "^2.1.3", - "get-tsconfig": "^4.13.6", - "obug": "^2.1.1" + "get-tsconfig": "^4.13.7", + "obug": "^2.1.1", + "picomatch": "^4.0.4" }, "engines": { "node": ">=20.19.0" @@ -2950,9 +2973,9 @@ }, "peerDependencies": { "@ts-macro/tsc": "^0.3.6", - "@typescript/native-preview": ">=7.0.0-dev.20250601.1", - "rolldown": "^1.0.0-rc.3", - "typescript": "^5.0.0 || ^6.0.0-beta", + "@typescript/native-preview": ">=7.0.0-dev.20260325.1", + "rolldown": "^1.0.0-rc.12", + "typescript": "^5.0.0 || ^6.0.0", "vue-tsc": "~3.2.0" }, "peerDependenciesMeta": { @@ -3212,9 +3235,9 @@ } }, "node_modules/tsdown": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.3.tgz", - "integrity": "sha512-oKKeMC8+IgNsB+81hvF5VBsqhqL/nr0g9vse+ujbK40vv0i3ReFI6gUts7hQH7J53ylQNjMgf2Vu6n0+P/uddA==", + "version": "0.21.7", + "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.7.tgz", + "integrity": "sha512-ukKIxKQzngkWvOYJAyptudclkm4VQqbjq+9HF5K5qDO8GJsYtMh8gIRwicbnZEnvFPr6mquFwYAVZ8JKt3rY2g==", "dev": true, "license": "MIT", "dependencies": { @@ -3222,18 +3245,18 @@ "cac": "^7.0.0", "defu": "^6.1.4", "empathic": "^2.0.0", - "hookable": "^6.0.1", + "hookable": "^6.1.0", "import-without-cache": "^0.2.5", "obug": "^2.1.1", - "picomatch": "^4.0.3", - "rolldown": "1.0.0-rc.9", - "rolldown-plugin-dts": "^0.22.5", + "picomatch": "^4.0.4", + "rolldown": "1.0.0-rc.12", + "rolldown-plugin-dts": "^0.23.2", "semver": "^7.7.4", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "tree-kill": "^1.2.2", "unconfig-core": "^7.5.0", - "unrun": "^0.2.32" + "unrun": "^0.2.34" }, "bin": { "tsdown": "dist/run.mjs" @@ -3246,11 +3269,11 @@ }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.21.3", - "@tsdown/exe": "0.21.3", + "@tsdown/css": "0.21.7", + "@tsdown/exe": "0.21.7", "@vitejs/devtools": "*", "publint": "^0.3.0", - "typescript": "^5.0.0", + "typescript": "^5.0.0 || ^6.0.0", "unplugin-unused": "^0.5.0" }, "peerDependenciesMeta": { @@ -3286,9 +3309,9 @@ "optional": true }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3347,13 +3370,13 @@ } }, "node_modules/unrun": { - "version": "0.2.32", - "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.32.tgz", - "integrity": "sha512-opd3z6791rf281JdByf0RdRQrpcc7WyzqittqIXodM/5meNWdTwrVxeyzbaCp4/Rgls/um14oUaif1gomO8YGg==", + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.34.tgz", + "integrity": "sha512-LyaghRBR++r7svhDK6tnDz2XaYHWdneBOA0jbS8wnRsHerI9MFljX4fIiTgbbNbEVzZ0C9P1OjWLLe1OqoaaEw==", "dev": true, "license": "MIT", "dependencies": { - "rolldown": "1.0.0-rc.9" + "rolldown": "1.0.0-rc.12" }, "bin": { "unrun": "dist/cli.mjs" @@ -3380,17 +3403,16 @@ "dev": true }, "node_modules/vite": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", - "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", - "picomatch": "^4.0.3", + "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.9", + "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "bin": { @@ -3407,8 +3429,8 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.0.0-alpha.31", - "esbuild": "^0.27.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", @@ -3458,6 +3480,298 @@ } } }, + "node_modules/vite/node_modules/@oxc-project/types": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "dev": true, + "license": "MIT" + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3473,20 +3787,54 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/vite/node_modules/rolldown": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + } + }, "node_modules/vitest": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", - "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", + "integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.0", - "@vitest/mocker": "4.1.0", - "@vitest/pretty-format": "4.1.0", - "@vitest/runner": "4.1.0", - "@vitest/snapshot": "4.1.0", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/expect": "4.1.4", + "@vitest/mocker": "4.1.4", + "@vitest/pretty-format": "4.1.4", + "@vitest/runner": "4.1.4", + "@vitest/snapshot": "4.1.4", + "@vitest/spy": "4.1.4", + "@vitest/utils": "4.1.4", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -3497,8 +3845,8 @@ "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -3514,13 +3862,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.0", - "@vitest/browser-preview": "4.1.0", - "@vitest/browser-webdriverio": "4.1.0", - "@vitest/ui": "4.1.0", + "@vitest/browser-playwright": "4.1.4", + "@vitest/browser-preview": "4.1.4", + "@vitest/browser-webdriverio": "4.1.4", + "@vitest/coverage-istanbul": "4.1.4", + "@vitest/coverage-v8": "4.1.4", + "@vitest/ui": "4.1.4", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -3541,6 +3891,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -3595,9 +3951,9 @@ } }, "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index e234285..f328f2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sqlite.org/sqlite-wasm", - "version": "3.51.2-build8", + "version": "3.53.0-build1", "description": "SQLite Wasm conveniently wrapped as an ES Module.", "type": "module", "repository": { @@ -63,19 +63,19 @@ "node": ">=22" }, "devDependencies": { - "@types/node": "^25.5.0", - "@typescript/native-preview": "^7.0.0-dev.20260315.1", - "@vitest/browser": "^4.1.0", - "@vitest/browser-playwright": "^4.1.0", - "happy-dom": "^20.8.4", + "@types/node": "^25.5.2", + "@typescript/native-preview": "^7.0.0-dev.20260409.1", + "@vitest/browser": "^4.1.4", + "@vitest/browser-playwright": "^4.1.4", + "happy-dom": "20.8.9", "http-server": "github:vapier/http-server", - "lefthook": "2.1.4", - "playwright": "^1.58.2", + "lefthook": "2.1.5", + "playwright": "^1.59.1", "prettier": "^3.8.1", "prettier-plugin-jsdoc": "^1.8.0", "publint": "^0.3.18", - "tsdown": "^0.21.3", - "typescript": "^5.9.3", - "vitest": "^4.1.0" + "tsdown": "^0.21.7", + "typescript": "^6.0.2", + "vitest": "^4.1.4" } } From 45460429823595d043d1b4f227559a0fdd8955c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:52:22 +0200 Subject: [PATCH 2/3] chore: update SQLite Wasm binaries from version-3.53.0 (4ebc7fdcf459e8d88eb5b019c2949bda86565528) (#22) Co-authored-by: jurerotar <28565137+jurerotar@users.noreply.github.com> --- src/bin/sqlite3-bundler-friendly.mjs | 2828 ++++++++++++++------------ src/bin/sqlite3-node.mjs | 441 ++-- src/bin/sqlite3-opfs-async-proxy.js | 652 ++++-- src/bin/sqlite3.mjs | 2820 +++++++++++++------------ src/bin/sqlite3.wasm | Bin 859730 -> 864752 bytes 5 files changed, 3861 insertions(+), 2880 deletions(-) diff --git a/src/bin/sqlite3-bundler-friendly.mjs b/src/bin/sqlite3-bundler-friendly.mjs index 0b99fc3..3fe8f41 100644 --- a/src/bin/sqlite3-bundler-friendly.mjs +++ b/src/bin/sqlite3-bundler-friendly.mjs @@ -27,11 +27,11 @@ /* @preserve ** This code was built from sqlite3 version... ** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** SQLITE_VERSION "3.53.0" +** SQLITE_VERSION_NUMBER 3053000 +** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** -** Emscripten SDK: 5.0.0 +** Emscripten SDK: 5.0.5 */ // This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. @@ -92,7 +92,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR /** This file was preprocessed using: - ./c-pp-lite -o ./bld/pre-js.bundler.js -Dtarget:es6-module -Dtarget:es6-bundler-friendly -DModule.instantiateWasm api/pre-js.c-pp.js + ./c-pp -o ./bld/pre-js.bundler.js -Dtarget:es6-module -Dtarget:es6-bundler-friendly -DModule.instantiateWasm api/pre-js.c-pp.js */ /* END FILE: api/pre-js.js. */ // end include: ./bld/pre-js.bundler.js @@ -211,37 +211,17 @@ var isFileURI = (filename) => filename.startsWith('file://'); // include: runtime_stack_check.js // end include: runtime_stack_check.js // include: runtime_exceptions.js +// Base Emscripten EH error class +class EmscriptenEH {} + +class EmscriptenSjLj extends EmscriptenEH {} + // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js var readyPromiseResolve, readyPromiseReject; // Memory management -var -/** @type {!Int8Array} */ - HEAP8, -/** @type {!Uint8Array} */ - HEAPU8, -/** @type {!Int16Array} */ - HEAP16, -/** @type {!Uint16Array} */ - HEAPU16, -/** @type {!Int32Array} */ - HEAP32, -/** @type {!Uint32Array} */ - HEAPU32, -/** @type {!Float32Array} */ - HEAPF32, -/** @type {!Float64Array} */ - HEAPF64; - -// BigInt64Array type is not correctly defined in closure -var -/** not-@type {!BigInt64Array} */ - HEAP64, -/* BigUint64Array type is not correctly defined in closure -/** not-@type {!BigUint64Array} */ - HEAPU64; var runtimeInitialized = false; @@ -339,11 +319,14 @@ function postRun() { // End ATPOSTRUNS hooks } -/** @param {string|number=} what */ +/** + * @param {string|number=} what + * @noreturn + */ function abort(what) { Module['onAbort']?.(what); - what = 'Aborted(' + what + ')'; + what = `Aborted(${what})`; // TODO(sbc): Should we remove printing and leave it up to whoever // catches the exception? err(what); @@ -514,6 +497,36 @@ async function createWasm() { } } + /** @type {!Int16Array} */ + var HEAP16; + + /** @type {!Int32Array} */ + var HEAP32; + + /** not-@type {!BigInt64Array} */ + var HEAP64; + + /** @type {!Int8Array} */ + var HEAP8; + + /** @type {!Float32Array} */ + var HEAPF32; + + /** @type {!Float64Array} */ + var HEAPF64; + + /** @type {!Uint16Array} */ + var HEAPU16; + + /** @type {!Uint32Array} */ + var HEAPU32; + + /** not-@type {!BigUint64Array} */ + var HEAPU64; + + /** @type {!Uint8Array} */ + var HEAPU8; + var callRuntimeCallbacks = (callbacks) => { while (callbacks.length > 0) { // Pass the module as the first argument. @@ -639,12 +652,9 @@ join2:(l, r) => PATH.normalize(l + '/' + r), var initRandomFill = () => { - return (view) => crypto.getRandomValues(view); - }; -var randomFill = (view) => { - // Lazily init on the first invocation. - (randomFill = initRandomFill())(view); + return (view) => (crypto.getRandomValues(view), 0); }; +var randomFill = (view) => (randomFill = initRandomFill())(view); @@ -1040,11 +1050,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node; node.stream_ops = MEMFS.ops_table.file.stream; - node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. - // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred - // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size - // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. - node.contents = null; + // The actual number of bytes used in the typed array, as opposed to + // contents.length which gives the whole capacity. + node.usedBytes = 0; + // The byte data of the file is stored in a typed array. + // Note: typed arrays are not resizable like normal JS arrays are, so + // there is a small penalty involved for appending file writes that + // continuously grow a file similar to std::vector capacity vs used. + node.contents = MEMFS.emptyFileContents ??= new Uint8Array(0); } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node; node.stream_ops = MEMFS.ops_table.link.stream; @@ -1061,36 +1074,29 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return node; }, getFileDataAsTypedArray(node) { - if (!node.contents) return new Uint8Array(0); - if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. - return new Uint8Array(node.contents); + return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. }, expandFileStorage(node, newCapacity) { - var prevCapacity = node.contents ? node.contents.length : 0; + var prevCapacity = node.contents.length; if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. - // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. - // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to - // avoid overshooting the allocation cap by a very large margin. + // Don't expand strictly to the given requested limit if it's only a very + // small increase, but instead geometrically grow capacity. + // For small filesizes (<1MB), perform size*2 geometric increase, but for + // large sizes, do a much more conservative size*1.125 increase to avoid + // overshooting the allocation cap by a very large margin. var CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); - if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. - var oldContents = node.contents; + if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. + var oldContents = MEMFS.getFileDataAsTypedArray(node); node.contents = new Uint8Array(newCapacity); // Allocate new storage. - if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. + node.contents.set(oldContents); }, resizeFileStorage(node, newSize) { if (node.usedBytes == newSize) return; - if (newSize == 0) { - node.contents = null; // Fully decommit when requesting a resize to zero. - node.usedBytes = 0; - } else { - var oldContents = node.contents; - node.contents = new Uint8Array(newSize); // Allocate new storage. - if (oldContents) { - node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. - } - node.usedBytes = newSize; - } + var oldContents = node.contents; + node.contents = new Uint8Array(newSize); // Allocate new storage. + node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. + node.usedBytes = newSize; }, node_ops:{ getattr(node) { @@ -1196,11 +1202,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var contents = stream.node.contents; if (position >= stream.node.usedBytes) return 0; var size = Math.min(stream.node.usedBytes - position, length); - if (size > 8 && contents.subarray) { // non-trivial, and typed array - buffer.set(contents.subarray(position, position + size), offset); - } else { - for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; - } + buffer.set(contents.subarray(position, position + size), offset); return size; }, write(stream, buffer, offset, length, position, canOwn) { @@ -1216,32 +1218,18 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var node = stream.node; node.mtime = node.ctime = Date.now(); - if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? - if (canOwn) { - node.contents = buffer.subarray(offset, offset + length); - node.usedBytes = length; - return length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. - node.contents = buffer.slice(offset, offset + length); - node.usedBytes = length; - return length; - } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? - node.contents.set(buffer.subarray(offset, offset + length), position); - return length; - } - } - - // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. - MEMFS.expandFileStorage(node, position+length); - if (node.contents.subarray && buffer.subarray) { + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + } else { + MEMFS.expandFileStorage(node, position+length); // Use typed array write which is available. node.contents.set(buffer.subarray(offset, offset + length), position); - } else { - for (var i = 0; i < length; i++) { - node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. - } + node.usedBytes = Math.max(node.usedBytes, position + length); } - node.usedBytes = Math.max(node.usedBytes, position + length); return length; }, llseek(stream, offset, whence) { @@ -1266,7 +1254,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var allocated; var contents = stream.node.contents; // Only make a new copy when MAP_PRIVATE is specified. - if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { + if (!(flags & 2) && contents.buffer === HEAP8.buffer) { // We can't emulate MAP_SHARED when the file is not backed by the // buffer we're mapping to (e.g. the HEAP buffer). allocated = false; @@ -1300,6 +1288,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; var FS_modeStringToFlags = (str) => { + if (typeof str != 'string') return str; var flagModes = { 'r': 0, 'r+': 2, @@ -1315,6 +1304,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return flags; }; + var FS_fileDataToTypedArray = (data) => { + if (typeof data == 'string') { + data = intArrayFromString(data, true); + } + if (!data.subarray) { + data = new Uint8Array(data); + } + return data; + }; + var FS_getMode = (canRead, canWrite) => { var mode = 0; if (canRead) mode |= 292 | 73; @@ -1412,8 +1411,6 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { ignorePermissions:true, filesystems:null, syncFSRequests:0, - readFiles:{ - }, ErrnoError:class { name = 'ErrnoError'; // We set the `name` property to be able to identify `FS.ErrnoError` @@ -1679,9 +1676,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // return 0 if any user, group or owner bits are set. if (perms.includes('r') && !(node.mode & 292)) { return 2; - } else if (perms.includes('w') && !(node.mode & 146)) { + } + if (perms.includes('w') && !(node.mode & 146)) { return 2; - } else if (perms.includes('x') && !(node.mode & 73)) { + } + if (perms.includes('x') && !(node.mode & 73)) { return 2; } return 0; @@ -1722,10 +1721,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10; } - } else { - if (FS.isDir(node.mode)) { - return 31; - } + } else if (FS.isDir(node.mode)) { + return 31; } return 0; }, @@ -1735,13 +1732,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } if (FS.isLink(node.mode)) { return 32; - } else if (FS.isDir(node.mode)) { - if (FS.flagsToPermissionString(flags) !== 'r' // opening for write - || (flags & (512 | 64))) { // TODO: check for O_SEARCH? (== search for dir only) + } + var mode = FS.flagsToPermissionString(flags); + if (FS.isDir(node.mode)) { + // opening for write + // TODO: check for O_SEARCH? (== search for dir only) + if (mode !== 'r' || (flags & (512 | 64))) { return 31; } } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + return FS.nodePermissions(node, mode); }, checkOpExists(op, err) { if (!op) { @@ -2309,7 +2309,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (path === "") { throw new FS.ErrnoError(44); } - flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags; + flags = FS_modeStringToFlags(flags); if ((flags & 64)) { mode = (mode & 4095) | 32768; } else { @@ -2396,11 +2396,6 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (created) { FS.chmod(node, mode & 0o777); } - if (Module['logReadFiles'] && !(flags & 1)) { - if (!(path in FS.readFiles)) { - FS.readFiles[path] = 1; - } - } return stream; }, close(stream) { @@ -2547,14 +2542,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { writeFile(path, data, opts = {}) { opts.flags = opts.flags || 577; var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data == 'string') { - data = new Uint8Array(intArrayFromString(data, true)); - } - if (ArrayBuffer.isView(data)) { - FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); - } else { - abort('Unsupported data type'); - } + data = FS_fileDataToTypedArray(data); + FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); FS.close(stream); }, cwd:() => FS.currentPath, @@ -2774,11 +2763,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var mode = FS_getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { - if (typeof data == 'string') { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } + data = FS_fileDataToTypedArray(data); // make sure we can write to the file FS.chmod(node, mode | 146); var stream = FS.open(node, 577); @@ -4102,6 +4087,7 @@ var _sqlite3_status64, _sqlite3_bind_null, _sqlite3_bind_pointer, _sqlite3_bind_text, + _sqlite3_bind_zeroblob, _sqlite3_bind_parameter_count, _sqlite3_bind_parameter_name, _sqlite3_bind_parameter_index, @@ -4365,6 +4351,7 @@ function assignWasmExports(wasmExports) { _sqlite3_bind_null = Module['_sqlite3_bind_null'] = wasmExports['sqlite3_bind_null']; _sqlite3_bind_pointer = Module['_sqlite3_bind_pointer'] = wasmExports['sqlite3_bind_pointer']; _sqlite3_bind_text = Module['_sqlite3_bind_text'] = wasmExports['sqlite3_bind_text']; + _sqlite3_bind_zeroblob = Module['_sqlite3_bind_zeroblob'] = wasmExports['sqlite3_bind_zeroblob']; _sqlite3_bind_parameter_count = Module['_sqlite3_bind_parameter_count'] = wasmExports['sqlite3_bind_parameter_count']; _sqlite3_bind_parameter_name = Module['_sqlite3_bind_parameter_name'] = wasmExports['sqlite3_bind_parameter_name']; _sqlite3_bind_parameter_index = Module['_sqlite3_bind_parameter_index'] = wasmExports['sqlite3_bind_parameter_index']; @@ -4709,6 +4696,7 @@ Module.runSQLite3PostLoadInit = async function( - sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls - sqlite3-vfs-opfs.c-pp.js => OPFS VFS - sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS + - sqlite3-vfs-opfs-wl.c-pp.js => WebLock-using OPFS VFS - post-js-footer.js => this file's epilogue And all of that gets sandwiched between extern-pre-js.js and @@ -4743,11 +4731,11 @@ Module.runSQLite3PostLoadInit = async function( /* @preserve ** This code was built from sqlite3 version... ** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** SQLITE_VERSION "3.53.0" +** SQLITE_VERSION_NUMBER 3053000 +** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** -** Emscripten SDK: 5.0.0 +** Emscripten SDK: 5.0.5 */ /* 2022-05-22 @@ -4850,6 +4838,13 @@ Module.runSQLite3PostLoadInit = async function( used in WASMFS-capable builds of the library (which the canonical builds do not include). + - `disable` (as of 3.53.0) may be an object with the following + properties: + - `vfs`, an object, may contain a map of VFS names to booleans. + Any mapping to falsy are disabled. The supported names + are: "kvvfs", "opfs", "opfs-sahpool", "opfs-wl". + - Other disabling options may be added in the future. + [^1] = This property may optionally be a function, in which case this function calls that function to fetch the value, enabling delayed evaluation. @@ -4896,7 +4891,8 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( ); return sqlite3ApiBootstrap.sqlite3; } - const config = Object.assign(Object.create(null),{ + const nu = (...obj)=>Object.assign(Object.create(null),...obj); + const config = nu({ exports: undefined, memory: undefined, bigIntEnabled: !!globalThis.BigInt64Array, @@ -4913,7 +4909,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( certain wasm.xWrap.resultAdapter()s. */ useStdAlloc: false - }, apiConfig || {}); + }, apiConfig); Object.assign(config, { allocExportName: config.useStdAlloc ? 'malloc' : 'sqlite3_malloc', @@ -4946,7 +4942,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( not documented are installed as 1-to-1 proxies for their C-side counterparts. */ - const capi = Object.create(null); + const capi = nu(); /** Holds state which are specific to the WASM-related infrastructure and glue code. @@ -4955,7 +4951,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( dynamically after the api object is fully constructed, so not all are documented in this file. */ - const wasm = Object.create(null); + const wasm = nu(); /** Internal helper for SQLite3Error ctor. */ const __rcStr = (rc)=>{ @@ -5503,6 +5499,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( toss: function(...args){throw new Error(args.join(' '))}, toss3, typedArrayPart: wasm.typedArrayPart, + nu, assert: function(arg,msg){ if( !arg ){ util.toss("Assertion failed:",msg); @@ -5759,7 +5756,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; }; } - const rc = Object.create(null), ov = [0,0]; + const rc = nu(), ov = [0,0]; let i = 0, k; while((k = capi.sqlite3_compileoption_get(i++))){ f._opt(k,ov); @@ -5767,7 +5764,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( } return f._result = rc; }else if(Array.isArray(optName)){ - const rc = Object.create(null); + const rc = nu(); optName.forEach((v)=>{ rc[v] = capi.sqlite3_compileoption_used(v); }); @@ -5818,7 +5815,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( The memory lives in the WASM heap and can be used with routines such as wasm.poke() and wasm.heap8u().slice(). */ - wasm.pstack = Object.assign(Object.create(null),{ + wasm.pstack = nu({ /** Sets the current pstack position to the given pointer. Results are undefined if the passed-in value did not come from @@ -6040,7 +6037,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( // sqlite3__wasm_init_wasmfs() is not available return this.dir = ""; } - }.bind(Object.create(null)); + }.bind(nu()); /** Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a @@ -6394,6 +6391,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: + case capi.SQLITE_DBCONFIG_FP_DIGITS: if( !this.ip ){ this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int', ['sqlite3*', 'int', 'int', '*']); @@ -6415,7 +6413,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( default: return capi.SQLITE_MISUSE; } - }.bind(Object.create(null)); + }.bind(nu()); /** Given a (sqlite3_value*), this function attempts to convert it @@ -6649,7 +6647,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( if(rc) return SQLite3Error.toss(rc,arguments[2]+"() failed with code "+rc); const pv = wasm.peekPtr(this.ptr); return pv ? capi.sqlite3_value_to_js( pv, true ) : undefined; - }.bind(Object.create(null)); + }.bind(nu()); /** A wrapper around sqlite3_preupdate_new() which fetches the @@ -6689,6 +6687,62 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( 'sqlite3changeset_old'); }/*changeset/preupdate additions*/ + /** + EXPERIMENTAL. For tentative addition in 3.53.0. + + sqlite3_js_retry_busy(maxTimes,callback[,beforeRetry]) + + Calls the given _synchronous_ callback function. If that function + returns sqlite3.capi.SQLITE_BUSY _or_ throws an SQLite3Error + with a resultCode property of that value then it will suppress + that error and try again, up to the given maximum number of + times. If the callback returns any other value than that, + it is returned. If the maximum number of retries has been + reached, an SQLite3Error with a resultCode value of + sqlite3.capi.SQLITE_BUSY is thrown. If the callback throws any + exception other than the aforementioned BUSY exception, it is + propagated. If it throws a BUSY exception on its final attempt, + that is propagated as well. + + If the beforeRetry argument is given, it must be a _synchronous_ + function. It is called immediately before each retry of the + callback (not for the initial call), passed the attempt number + (so it starts with 2, not 1). If it throws, the exception is + handled as described above. Its result value is ignored. + + To effectively retry "forever", pass a negative maxTimes value, + with the caveat that there is no recovery from that unless the + beforeRetry() can figure out when to throw. + + TODO: an async variant of this. + */ + capi.sqlite3_js_retry_busy = function(maxTimes, callback, beforeRetry){ + for(let n = 1; n <= maxTimes; ++n){ + try{ + if( beforeRetry && n>1 ) beforeRetry(n); + const rc = callback(); + if( capi.SQLITE_BUSY===rc ){ + if( n===maxTimes ){ + throw new SQLite3Error(rc, [ + "sqlite3_js_retry_busy() max retry attempts (", + maxTimes, + ") reached." + ].join('')); + } + continue; + } + return rc; + }catch(e){ + if( n{}; const debug = sqlite3.__isUnderTest - ? (...args)=>sqlite3.config.debug("kvvfs:", ...args) + ? (...args)=>sqlite3.config.debug?.("kvvfs:", ...args) : noop; - const warn = (...args)=>sqlite3.config.warn("kvvfs:", ...args); - const error = (...args)=>sqlite3.config.error("kvvfs:", ...args); + const warn = (...args)=>sqlite3.config.warn?.("kvvfs:", ...args); + const error = (...args)=>sqlite3.config.error?.("kvvfs:", ...args); /** Implementation of JS's Storage interface for use as backing store @@ -16179,17 +16240,21 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ and recreating it whenever a property index might be invalidated. */ class KVVfsStorage { - #map; - #keys; - #getKeys(){return this.#keys ??= Object.keys(this.#map);} + #map = Object.create(null); + #keys = null; + #size = 0; constructor(){ this.clear(); } + #getKeys(){ + return this.#keys ??= Object.keys(this.#map); + } + key(n){ - const k = this.#getKeys(); - return n= this.#size) return null; + return this.#getKeys()[n]; } getItem(k){ @@ -16197,14 +16262,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } setItem(k,v){ - if( !hop(this.#map, k) ){ + if( !(k in this.#map) ){ + ++this.#size; this.#keys = null; } this.#map[k] = ''+v; } removeItem(k){ - if( delete this.#map[k] ){ + if( k in this.#map ){ + delete this.#map[k]; + --this.#size; this.#keys = null; } } @@ -16212,10 +16280,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ clear(){ this.#map = Object.create(null); this.#keys = null; + this.#size = 0; } get length() { - return this.#getKeys().length; + return this.#size; } }/*KVVfsStorage*/; @@ -16977,36 +17046,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }, - // We override xRead/xWrite only for logging/debugging. They - // should otherwise be disabled (it's faster that way). - xRead: function(pFile,pTgt,n,iOff64){ - cache.popError(); - try{ - if( kvvfs?.log?.xRead ){ - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xRead", n, iOff64, h); - } - return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); - }catch(e){ - error("xRead",e); - return cache.setError(e); - } - }, - xWrite: function(pFile,pSrc,n,iOff64){ - cache.popError(); - try{ - if( kvvfs?.log?.xWrite ){ - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xWrite", n, iOff64, h); - } - return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); - }catch(e){ - error("xWrite",e); - return cache.setError(e); - } - }, }/*.ioDb*/, @@ -17018,9 +17057,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }/*.ioJrnl*/ }/*methodOverrides*/; - debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, - kvvfsMethods, capi.sqlite3_file.structInfo, - KVVfsFile.structInfo); + try { util.assert( cache.buffer.n>1024*129, "Heap buffer is not large enough" /* Native is SQLITE_KVOS_SZ is 133073 as of this writing */ ); @@ -17109,7 +17146,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ limitation which has since been overcome, but removal of JsStorageDb.prototype.clearStorage() would be a backwards compatibility break, so this function permits wiping the storage for those two - cases even if they are opened. Use with case. + cases even if they are opened. Use with care. */ const sqlite3_js_kvvfs_clear = function callee(which){ if( ''===which ){ @@ -17784,7 +17821,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } return rc; }catch(e){ - return VT.xErrror('xConnect', e, capi.SQLITE_ERROR); + return VT.xError('xConnect', e, capi.SQLITE_ERROR); } }, xCreate: wasm.ptr.null, // eponymous only @@ -17883,7 +17920,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ })/*globalThis.sqlite3ApiBootstrap.initializers*/; /* - 2022-09-18 + 2026-03-04 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: @@ -17894,283 +17931,584 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ *********************************************************************** - This file holds the synchronous half of an sqlite3_vfs - implementation which proxies, in a synchronous fashion, the - asynchronous Origin-Private FileSystem (OPFS) APIs using a second - Worker, implemented in sqlite3-opfs-async-proxy.js. This file is - intended to be appended to the main sqlite3 JS deliverable somewhere - after sqlite3-api-oo1.js. + This file holds code shared by sqlite3-vfs-opfs{,-wl}.c-pp.js. It + creates a private/internal sqlite3.opfs namespace common to the two + and used (only) by them and the test framework. It is not part of + the public API. The library deletes sqlite3.opfs in its final + bootstrapping steps unless it's specifically told to keep them (for + testing purposes only) using an undocumented and unsupported + mechanism. */ -'use strict'; globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ -/** - installOpfsVfs() returns a Promise which, on success, installs an - sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs - which accept a VFS. It is intended to be called via - sqlite3ApiBootstrap.initializers or an equivalent mechanism. - - The installed VFS uses the Origin-Private FileSystem API for - all file storage. On error it is rejected with an exception - explaining the problem. Reasons for rejection include, but are - not limited to: + 'use strict'; + if( sqlite3.config.disable?.vfs?.opfs && + sqlite3.config.disable.vfs['opfs-vfs'] ){ + return; + } + const toss = sqlite3.util.toss, + capi = sqlite3.capi, + util = sqlite3.util, + wasm = sqlite3.wasm; - - The counterpart Worker (see below) could not be loaded. + /** + Generic utilities for working with OPFS. This will get filled out + by the Promise setup and, on success, installed as sqlite3.opfs. - - The environment does not support OPFS. That includes when - this function is called from the main window thread. + This is an internal/private namespace intended for use solely by + the OPFS VFSes and test code for them. The library bootstrapping + process removes this object in non-testing contexts. + */ + const opfsUtil = sqlite3.opfs = Object.create(null); - Significant notes and limitations: + /** + Returns true if _this_ thread has access to the OPFS APIs. + */ + opfsUtil.thisThreadHasOPFS = ()=>{ + return globalThis.FileSystemHandle && + globalThis.FileSystemDirectoryHandle && + globalThis.FileSystemFileHandle && + globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && + navigator?.storage?.getDirectory; + }; - - The OPFS features used here are only available in dedicated Worker - threads. This file tries to detect that case, resulting in a - rejected Promise if those features do not seem to be available. + /** + Must be called by the OPFS VFSes immediately after they determine + whether OPFS is available by calling + thisThreadHasOPFS(). Resolves to the OPFS storage root directory + and sets opfsUtil.rootDirectory to that value. + */ + opfsUtil.getRootDir = async function f(){ + return f.promise ??= navigator.storage.getDirectory().then(d=>{ + opfsUtil.rootDirectory = d; + return d; + }).catch(e=>{ + delete f.promise; + throw e; + }); + }; - - It requires the SharedArrayBuffer and Atomics classes, and the - former is only available if the HTTP server emits the so-called - COOP and COEP response headers. These features are required for - proxying OPFS's synchronous API via the synchronous interface - required by the sqlite3_vfs API. + /** + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd arg + is true, the result is returned as an array of path elements, + else an absolute path string is returned. + */ + opfsUtil.getResolvedPath = function(filename,splitIt){ + const p = new URL(filename, "file://irrelevant").pathname; + return splitIt ? p.split('/').filter((v)=>!!v) : p; + }; - - This function may only be called a single time. When called, this - function removes itself from the sqlite3 object. + /** + Takes the absolute path to a filesystem element. Returns an + array of [handleOfContainingDir, filename]. If the 2nd argument + is truthy then each directory element leading to the file is + created along the way. Throws if any creation or resolution + fails. + */ + opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false){ + const path = opfsUtil.getResolvedPath(absFilename, true); + const filename = path.pop(); + let dh = await opfsUtil.getRootDir(); + for(const dirName of path){ + if(dirName){ + dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); + } + } + return [dh, filename]; + }; - All arguments to this function are for internal/development purposes - only. They do not constitute a public API and may change at any - time. + /** + Creates the given directory name, recursively, in + the OPFS filesystem. Returns true if it succeeds or the + directory already exists, else false. + */ + opfsUtil.mkdir = async function(absDirName){ + try { + await opfsUtil.getDirForFilename(absDirName+"/filepart", true); + return true; + }catch(e){ + //sqlite3.config.warn("mkdir(",absDirName,") failed:",e); + return false; + } + }; - The argument may optionally be a plain object with the following - configuration options: + /** + Checks whether the given OPFS filesystem entry exists, + returning true if it does, false if it doesn't or if an + exception is intercepted while trying to make the + determination. + */ + opfsUtil.entryExists = async function(fsEntryName){ + try { + const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); + await dh.getFileHandle(fn); + return true; + }catch(e){ + return false; + } + }; - - proxyUri: name of the async proxy JS file. + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + opfsUtil.randomFilename = function f(len=16){ + if(!f._chars){ + f._chars = "abcdefghijklmnopqrstuvwxyz"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for( ; i < len; ++i){ + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(""); + /* + An alternative impl. with an unpredictable length + but much simpler: - - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables - logging of errors. 2 enables logging of warnings and errors. 3 - additionally enables debugging info. Logging is performed - via the sqlite3.config.{log|warn|error}() functions. + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + */ + }; - - sanityChecks (=false): if true, some basic sanity tests are run on - the OPFS VFS API after it's initialized, before the returned - Promise resolves. This is only intended for testing and - development of the VFS, not client-side use. + /** + Returns a promise which resolves to an object which represents + all files and directories in the OPFS tree. The top-most object + has two properties: `dirs` is an array of directory entries + (described below) and `files` is a list of file names for all + files in that directory. - On success, the Promise resolves to the top-most sqlite3 namespace - object and that object gets a new object installed in its - `opfs` property, containing several OPFS-specific utilities. -*/ -const installOpfsVfs = function callee(options){ - if(!globalThis.SharedArrayBuffer - || !globalThis.Atomics){ - return Promise.reject( - new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. "+ - "The server must emit the COOP/COEP response headers to enable those. "+ - "See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep") - ); - }else if('undefined'===typeof WorkerGlobalScope){ - return Promise.reject( - new Error("The OPFS sqlite3_vfs cannot run in the main thread "+ - "because it requires Atomics.wait().") - ); - }else if(!globalThis.FileSystemHandle || - !globalThis.FileSystemDirectoryHandle || - !globalThis.FileSystemFileHandle || - !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || - !navigator?.storage?.getDirectory){ - return Promise.reject( - new Error("Missing required OPFS APIs.") - ); - } - if(!options || 'object'!==typeof options){ - options = Object.create(null); - } - const urlParams = new URL(globalThis.location.href).searchParams; - if(urlParams.has('opfs-disable')){ - //sqlite3.config.warn('Explicitly not installing "opfs" VFS due to opfs-disable flag.'); - return Promise.resolve(sqlite3); - } - if(undefined===options.verbose){ - options.verbose = urlParams.has('opfs-verbose') - ? (+urlParams.get('opfs-verbose') || 2) : 1; - } - if(undefined===options.sanityChecks){ - options.sanityChecks = urlParams.has('opfs-sanity-check'); - } - if(undefined===options.proxyUri){ - options.proxyUri = callee.defaultProxyUri; - } + Traversal starts at sqlite3.opfs.rootDirectory. - //sqlite3.config.warn("OPFS options =",options,globalThis.location); + Each `dirs` entry is an object in this form: - if('function' === typeof options.proxyUri){ - options.proxyUri = options.proxyUri(); - } - const thePromise = new Promise(function(promiseResolve_, promiseReject_){ - const loggers = [ - sqlite3.config.error, - sqlite3.config.warn, - sqlite3.config.log - ]; - const logImpl = (level,...args)=>{ - if(options.verbose>level) loggers[level]("OPFS syncer:",...args); - }; - const log = (...args)=>logImpl(2, ...args); - const warn = (...args)=>logImpl(1, ...args); - const error = (...args)=>logImpl(0, ...args); - const toss = sqlite3.util.toss; - const capi = sqlite3.capi; - const util = sqlite3.util; - const wasm = sqlite3.wasm; - const sqlite3_vfs = capi.sqlite3_vfs; - const sqlite3_file = capi.sqlite3_file; - const sqlite3_io_methods = capi.sqlite3_io_methods; - /** - Generic utilities for working with OPFS. This will get filled out - by the Promise setup and, on success, installed as sqlite3.opfs. + ``` + { name: directoryName, + dirs: [...subdirs], + files: [...file names] + } + ``` - ACHTUNG: do not rely on these APIs in client code. They are - experimental and subject to change or removal as the - OPFS-specific sqlite3_vfs evolves. - */ - const opfsUtil = Object.create(null); + The `files` and `subdirs` entries are always set but may be + empty arrays. - /** - Returns true if _this_ thread has access to the OPFS APIs. - */ - const thisThreadHasOPFS = ()=>{ - return globalThis.FileSystemHandle && - globalThis.FileSystemDirectoryHandle && - globalThis.FileSystemFileHandle && - globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && - navigator?.storage?.getDirectory; - }; + The returned object has the same structure but its `name` is + an empty string. All returned objects are created with + Object.create(null), so have no prototype. - /** - Not part of the public API. Solely for internal/development - use. - */ - opfsUtil.metrics = { - dump: function(){ - let k, n = 0, t = 0, w = 0; - for(k in state.opIds){ - const m = metrics[k]; - n += m.count; - t += m.time; - w += m.wait; - m.avgTime = (m.count && m.time) ? (m.time / m.count) : 0; - m.avgWait = (m.count && m.wait) ? (m.wait / m.count) : 0; - } - sqlite3.config.log(globalThis.location.href, - "metrics for",globalThis.location.href,":",metrics, - "\nTotal of",n,"op(s) for",t, - "ms (incl. "+w+" ms of waiting on the async side)"); - sqlite3.config.log("Serialization metrics:",metrics.s11n); - W.postMessage({type:'opfs-async-metrics'}); - }, - reset: function(){ - let k; - const r = (m)=>(m.count = m.time = m.wait = 0); - for(k in state.opIds){ - r(metrics[k] = Object.create(null)); + Design note: the entries do not contain more information, + e.g. file sizes, because getting such info is not only + expensive but is subject to locking-related errors. + */ + opfsUtil.treeList = async function(){ + const doDir = async function callee(dirHandle,tgt){ + tgt.name = dirHandle.name; + tgt.dirs = []; + tgt.files = []; + for await (const handle of dirHandle.values()){ + if('directory' === handle.kind){ + const subDir = Object.create(null); + tgt.dirs.push(subDir); + await callee(handle, subDir); + }else{ + tgt.files.push(handle.name); } - let s = metrics.s11n = Object.create(null); - s = s.serialize = Object.create(null); - s.count = s.time = 0; - s = metrics.s11n.deserialize = Object.create(null); - s.count = s.time = 0; } - }/*metrics*/; - const opfsIoMethods = new sqlite3_io_methods(); - const opfsVfs = new sqlite3_vfs() - .addOnDispose( ()=>opfsIoMethods.dispose()); - let promiseWasRejected = undefined; - const promiseReject = (err)=>{ - promiseWasRejected = true; - opfsVfs.dispose(); - return promiseReject_(err); }; - const promiseResolve = ()=>{ - promiseWasRejected = false; - return promiseResolve_(sqlite3); + const root = Object.create(null); + const dir = await opfsUtil.getRootDir(); + await doDir(dir, root); + return root; + }; + + /** + Irrevocably deletes _all_ files in the current origin's OPFS. + Obviously, this must be used with great caution. It may throw + an exception if removal of anything fails (e.g. a file is + locked), but the precise conditions under which the underlying + APIs will throw are not documented (so we cannot tell you what + they are). + */ + opfsUtil.rmfr = async function(){ + const rd = await opfsUtil.getRootDir(); + const dir = rd, opt = {recurse: true}; + for await (const handle of dir.values()){ + dir.removeEntry(handle.name, opt); + } + }; + + /** + Deletes the given OPFS filesystem entry. As this environment + has no notion of "current directory", the given name must be an + absolute path. If the 2nd argument is truthy, deletion is + recursive (use with caution!). + + The returned Promise resolves to true if the deletion was + successful, else false (but...). The OPFS API reports the + reason for the failure only in human-readable form, not + exceptions which can be type-checked to determine the + failure. Because of that... + + If the final argument is truthy then this function will + propagate any exception on error, rather than returning false. + */ + opfsUtil.unlink = async function(fsEntryName, recursive = false, + throwOnError = false){ + try { + const [hDir, filenamePart] = + await opfsUtil.getDirForFilename(fsEntryName, false); + await hDir.removeEntry(filenamePart, {recursive}); + return true; + }catch(e){ + if(throwOnError){ + throw new Error("unlink(",arguments[0],") failed: "+e.message,{ + cause: e + }); + } + return false; + } + }; + + /** + Traverses the OPFS filesystem, calling a callback for each + entry. The argument may be either a callback function or an + options object with any of the following properties: + + - `callback`: function which gets called for each filesystem + entry. It gets passed 3 arguments: 1) the + FileSystemFileHandle or FileSystemDirectoryHandle of each + entry (noting that both are instanceof FileSystemHandle). 2) + the FileSystemDirectoryHandle of the parent directory. 3) the + current depth level, with 0 being at the top of the tree + relative to the starting directory. If the callback returns a + literal false, as opposed to any other falsy value, traversal + stops without an error. Any exceptions it throws are + propagated. Results are undefined if the callback manipulate + the filesystem (e.g. removing or adding entries) because the + how OPFS iterators behave in the face of such changes is + undocumented. + + - `recursive` [bool=true]: specifies whether to recurse into + subdirectories or not. Whether recursion is depth-first or + breadth-first is unspecified! + + - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] + specifies the starting directory. + + If this function is passed a function, it is assumed to be the + callback. + + Returns a promise because it has to (by virtue of being async) + but that promise has no specific meaning: the traversal it + performs is synchronous. The promise must be used to catch any + exceptions propagated by the callback, however. + */ + opfsUtil.traverse = async function(opt){ + const defaultOpt = { + recursive: true, + directory: await opfsUtil.getRootDir() }; - const W = - new Worker(new URL("sqlite3-opfs-async-proxy.js", import.meta.url)); - setTimeout(()=>{ - /* At attempt to work around a browser-specific quirk in which - the Worker load is failing in such a way that we neither - resolve nor reject it. This workaround gives that resolve/reject - a time limit and rejects if that timer expires. Discussion: - https://sqlite.org/forum/forumpost/a708c98dcb3ef */ - if(undefined===promiseWasRejected){ - promiseReject( - new Error("Timeout while waiting for OPFS async proxy worker.") - ); + if('function'===typeof opt){ + opt = {callback:opt}; + } + opt = Object.assign(defaultOpt, opt||{}); + const doDir = async function callee(dirHandle, depth){ + for await (const handle of dirHandle.values()){ + if(false === opt.callback(handle, dirHandle, depth)) return false; + else if(opt.recursive && 'directory' === handle.kind){ + if(false === await callee(handle, depth + 1)) break; + } } - }, 4000); - W._originalOnError = W.onerror /* will be restored later */; - W.onerror = function(err){ - // The error object doesn't contain any useful info when the - // failure is, e.g., that the remote script is 404. - error("Error initializing OPFS asyncer:",err); - promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); }; - const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; - const dVfs = pDVfs - ? new sqlite3_vfs(pDVfs) - : null /* dVfs will be null when sqlite3 is built with - SQLITE_OS_OTHER. */; + doDir(opt.directory, 0); + }; + + /** + Impl of opfsUtil.importDb() when it's given a function as its + second argument. + */ + const importDbChunked = async function(filename, callback){ + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + const hFile = await hDir.getFileHandle(fnamePart, {create:true}); + let sah = await hFile.createSyncAccessHandle(); + let nWrote = 0, chunk, checkedHeader = false, err = false; + try{ + sah.truncate(0); + while( undefined !== (chunk = await callback()) ){ + if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if( !checkedHeader && 0===nWrote && chunk.byteLength>=15 ){ + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, {at: nWrote}); + nWrote += chunk.byteLength; + } + if( nWrote < 512 || 0!==nWrote % 512 ){ + toss("Input size",nWrote,"is not correct for an SQLite database."); + } + if( !checkedHeader ){ + const header = new Uint8Array(20); + sah.read( header, {at: 0} ); + util.affirmDbHeader( header ); + } + sah.write(new Uint8Array([1,1]), {at: 18}/*force db out of WAL mode*/); + return nWrote; + }catch(e){ + await sah.close(); + sah = undefined; + await hDir.removeEntry( fnamePart ).catch(()=>{}); + throw e; + }finally { + if( sah ) await sah.close(); + } + }; + + /** + Asynchronously imports the given bytes (a byte array or + ArrayBuffer) into the given database file. + + Results are undefined if the given db name refers to an opened + db. + + If passed a function for its second argument, its behaviour + changes: imports its data in chunks fed to it by the given + callback function. It calls the callback (which may be async) + repeatedly, expecting either a Uint8Array or ArrayBuffer (to + denote new input) or undefined (to denote EOF). For so long as + the callback continues to return non-undefined, it will append + incoming data to the given VFS-hosted database file. When + called this way, the resolved value of the returned Promise is + the number of bytes written to the target file. + + It very specifically requires the input to be an SQLite3 + database and throws if that's not the case. It does so in + order to prevent this function from taking on a larger scope + than it is specifically intended to. i.e. we do not want it to + become a convenience for importing arbitrary files into OPFS. + + This routine rewrites the database header bytes in the output + file (not the input array) to force disabling of WAL mode. + + On error this throws and the state of the input file is + undefined (it depends on where the exception was triggered). + + On success, resolves to the number of bytes written. + */ + opfsUtil.importDb = async function(filename, bytes){ + if( bytes instanceof Function ){ + return importDbChunked(filename, bytes); + } + if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + util.affirmIsDb(bytes); + const n = bytes.byteLength; + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + let sah, err, nWrote = 0; + try { + const hFile = await hDir.getFileHandle(fnamePart, {create:true}); + sah = await hFile.createSyncAccessHandle(); + sah.truncate(0); + nWrote = sah.write(bytes, {at: 0}); + if(nWrote != n){ + toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); + } + sah.write(new Uint8Array([1,1]), {at: 18}) /* force db out of WAL mode */; + return nWrote; + }catch(e){ + if( sah ){ await sah.close(); sah = undefined; } + await hDir.removeEntry( fnamePart ).catch(()=>{}); + throw e; + }finally{ + if( sah ) await sah.close(); + } + }; + + /** + Checks for features required for OPFS VFSes and throws with a + descriptive error message if they're not found. This is intended + to be run as part of async VFS installation steps. + */ + opfsUtil.vfsInstallationFeatureCheck = function(vfsName){ + if( !globalThis.SharedArrayBuffer || !globalThis.Atomics ){ + toss("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics.", + "The server must emit the COOP/COEP response headers to enable those.", + "See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep"); + }else if( 'undefined'===typeof WorkerGlobalScope ){ + toss("The OPFS sqlite3_vfs cannot run in the main thread", + "because it requires Atomics.wait()."); + }else if( !globalThis.FileSystemHandle || + !globalThis.FileSystemDirectoryHandle || + !globalThis.FileSystemFileHandle?.prototype?.createSyncAccessHandle || + !navigator?.storage?.getDirectory ){ + toss("Missing required OPFS APIs."); + }else if( 'opfs-wl'===vfsName && !globalThis.Atomics.waitAsync ){ + toss('The',vfsName,'VFS requires Atomics.waitAsync(), which is not available.'); + } + }; + + /** + Must be called by the VFS's main installation routine and passed + the options object that function receives and a reference to that + function itself (we don't need this anymore). + + It throws if OPFS is not available. + + If it returns falsy, it detected that OPFS should be disabled, in + which case the callee should immediately return/resolve to the + sqlite3 object. + + Else it returns a new copy of the options object, fleshed out + with any missing defaults. The caller must: + + - Set up any local state they need. + + - Call opfsUtil.createVfsState(vfsName,opt), where opt is the + object returned by this function. + + - Set up any references they may need to state returned + by the previous step. + + - Call opfvs.bindVfs() + */ + opfsUtil.initOptions = function callee(vfsName, options){ + const urlParams = new URL(globalThis.location.href).searchParams; + if( urlParams.has(vfsName+'-disable') ){ + //sqlite3.config.warn('Explicitly not installing "opfs" VFS due to opfs-disable flag.'); + return; + } + try{ + opfsUtil.vfsInstallationFeatureCheck(vfsName); + }catch(e){ + return; + } + options = util.nu(options); + options.vfsName = vfsName; + options.verbose ??= urlParams.has('opfs-verbose') + ? +urlParams.get('opfs-verbose') : 1; + options.sanityChecks ??= urlParams.has('opfs-sanity-check'); + + if( !opfsUtil.proxyUri ){ + opfsUtil.proxyUri = "sqlite3-opfs-async-proxy.js"; + if( sqlite3.scriptInfo?.sqlite3Dir ){ + /* Doing this from one scope up, outside of this function, does + not work. */ + opfsUtil.proxyUri = ( + sqlite3.scriptInfo.sqlite3Dir + opfsUtil.proxyUri + ); + } + } + options.proxyUri ??= opfsUtil.proxyUri; + if('function' === typeof options.proxyUri){ + options.proxyUri = options.proxyUri(); + } + //sqlite3.config.warn("opfsUtil options =",JSON.stringify(options), 'urlParams =', urlParams); + return opfsUtil.options = options; + }; + + /** + Creates, populates, and returns the main state object used by the + "opfs" and "opfs-wl" VFSes, and transfered from those to their + async counterparts. + + The returned object's vfs property holds the fully-populated + capi.sqlite3_vfs instance, tagged with lots of extra state which + the current VFSes need to have exposed to them. + + After setting up any local state needed, the caller must call + theVfs.bindVfs(X,Y), where X is an object containing the + sqlite3_io_methods to override and Y is a callback which gets + triggered if init succeeds, before the final Promise decides + whether or not to reject. + + This object must, when it's passed to the async part, contain + only cloneable or sharable objects. After the worker's "inited" + message arrives, other types of data may be added to it. + */ + opfsUtil.createVfsState = function(){ + const state = util.nu(); + const options = opfsUtil.options; + state.verbose = options.verbose; + + const loggers = [ + sqlite3.config.error, + sqlite3.config.warn, + sqlite3.config.log + ]; + const vfsName = options.vfsName + || toss("Maintenance required: missing VFS name"); + const logImpl = (level,...args)=>{ + if(state.verbose>level) loggers[level](vfsName+":",...args); + }; + const log = (...args)=>logImpl(2, ...args), + warn = (...args)=>logImpl(1, ...args), + error = (...args)=>logImpl(0, ...args), + capi = sqlite3.capi, + wasm = sqlite3.wasm; + + const opfsVfs = state.vfs = new capi.sqlite3_vfs(); + const opfsIoMethods = opfsVfs.ioMethods = new capi.sqlite3_io_methods(); + opfsIoMethods.$iVersion = 1; opfsVfs.$iVersion = 2/*yes, two*/; opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; opfsVfs.$mxPathname = 1024/* sure, why not? The OPFS name length limit is undocumented/unspecified. */; - opfsVfs.$zName = wasm.allocCString("opfs"); - // All C-side memory of opfsVfs is zeroed out, but just to be explicit: - opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; + opfsVfs.$zName = wasm.allocCString(vfsName); opfsVfs.addOnDispose( - '$zName', opfsVfs.$zName, - 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null) + '$zName', opfsVfs.$zName, opfsIoMethods + /** + Pedantic sidebar: the entries in this array are items to + clean up when opfsVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown + order of the wasm engine and the JS one are undefined so + there is no guaranty that the opfsVfs instance would be + available in one environment or the other when + sqlite3_os_end() is called (_if_ it gets called at all in a + wasm build, which is undefined). i.e. addOnDispose() here is + a matter of "correctness", not necessity. It just wouldn't do + to leave the impression that we're blindly leaking memory. + */ ); - /** - Pedantic sidebar about opfsVfs.ondispose: the entries in that array - are items to clean up when opfsVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the opfsVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - /** - State which we send to the async-api Worker or share with it. - This object must initially contain only cloneable or sharable - objects. After the worker's "inited" message arrives, other types - of data may be added to it. - For purposes of Atomics.wait() and Atomics.notify(), we use a - SharedArrayBuffer with one slot reserved for each of the API - proxy's methods. The sync side of the API uses Atomics.wait() - on the corresponding slot and the async side uses - Atomics.notify() on that slot. - - The approach of using a single SAB to serialize comms for all - instances might(?) lead to deadlock situations in multi-db - cases. We should probably have one SAB here with a single slot - for locking a per-file initialization step and then allocate a - separate SAB like the above one for each file. That will - require a bit of acrobatics but should be feasible. The most - problematic part is that xOpen() would have to use - postMessage() to communicate its SharedArrayBuffer, and mixing - that approach with Atomics.wait/notify() gets a bit messy. - */ - const state = Object.create(null); - state.verbose = options.verbose; - state.littleEndian = (()=>{ - const buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true /* ==>littleEndian */); - // Int16Array uses the platform's endianness. - return new Int16Array(buffer)[0] === 256; - })(); + opfsVfs.metrics = util.nu({ + counters: util.nu(), + dump: function(){ + let k, n = 0, t = 0, w = 0; + for(k in state.opIds){ + const m = metrics[k]; + n += m.count; + t += m.time; + w += m.wait; + m.avgTime = (m.count && m.time) ? (m.time / m.count) : 0; + m.avgWait = (m.count && m.wait) ? (m.wait / m.count) : 0; + } + sqlite3.config.log(globalThis.location.href, + "metrics for",globalThis.location.href,":",metrics, + "\nTotal of",n,"op(s) for",t, + "ms (incl. "+w+" ms of waiting on the async side)"); + sqlite3.config.log("Serialization metrics:",opfsVfs.metrics.counters.s11n); + opfsVfs.worker?.postMessage?.({type:'opfs-async-metrics'}); + }, + reset: function(){ + let k; + const r = (m)=>(m.count = m.time = m.wait = 0); + const m = opfsVfs.metrics.counters; + for(k in state.opIds){ + r(m[k] = Object.create(null)); + } + let s = m.s11n = Object.create(null); + s = s.serialize = Object.create(null); + s.count = s.time = 0; + s = m.s11n.deserialize = Object.create(null); + s.count = s.time = 0; + } + })/*opfsVfs.metrics*/; + /** asyncIdleWaitTime is how long (ms) to wait, in the async proxy, for each Atomics.wait() when waiting on inbound VFS API calls. @@ -18191,7 +18529,8 @@ const installOpfsVfs = function callee(options){ 0 = no exception logging. 1 = only log exceptions for "significant" ops like xOpen(), - xRead(), and xWrite(). + xRead(), and xWrite(). Exceptions related to, e.g., wait/retry + loops in acquiring SyncAccessHandles are not logged. 2 = log all exceptions. */ @@ -18217,22 +18556,31 @@ const installOpfsVfs = function callee(options){ state.fileBufferSize/* file i/o block */ + state.sabS11nSize/* argument/result serialization block */ ); + + /** + For purposes of Atomics.wait() and Atomics.notify(), we use a + SharedArrayBuffer with one slot reserved for each of the API + proxy's methods. The sync side of the API uses Atomics.wait() + on the corresponding slot and the async side uses + Atomics.notify() on that slot. state.opIds holds the SAB slot + IDs of each of those. + */ state.opIds = Object.create(null); - const metrics = Object.create(null); { /* Indexes for use in our SharedArrayBuffer... */ let i = 0; /* SAB slot used to communicate which operation is desired between both workers. This worker writes to it and the other - listens for changes. */ + listens for changes and clears it. The values written to it + are state.opIds.x[A-Z][a-z]+, defined below.*/ state.opIds.whichOp = i++; - /* Slot for storing return values. This worker listens to that - slot and the other worker writes to it. */ + /* Slot for storing return values. This side listens to that + slot and the async proxy writes to it. */ state.opIds.rc = i++; - /* Each function gets an ID which this worker writes to - the whichOp slot. The async-api worker uses Atomic.wait() - on the whichOp slot to figure out which operation to run - next. */ + /* Each function gets an ID which this worker writes to the + state.opIds.whichOp slot. The async-api worker uses + Atomic.wait() on the whichOp slot to figure out which + operation to run next. */ state.opIds.xAccess = i++; state.opIds.xClose = i++; state.opIds.xDelete = i++; @@ -18246,24 +18594,28 @@ const installOpfsVfs = function callee(options){ state.opIds.xTruncate = i++; state.opIds.xUnlock = i++; state.opIds.xWrite = i++; - state.opIds.mkdir = i++; + state.opIds.mkdir = i++ /*currently unused*/; + /** Internal signals which are used only during development and + testing via the dev console. */ state.opIds['opfs-async-metrics'] = i++; state.opIds['opfs-async-shutdown'] = i++; /* The retry slot is used by the async part for wait-and-retry - semantics. Though we could hypothetically use the xSleep slot - for that, doing so might lead to undesired side effects. */ + semantics. It is never written to, only used as a convenient + place to wait-with-timeout for a value which will never be + written, i.e. sleep()ing, before retrying a failed attempt to + acquire a SharedAccessHandle. */ state.opIds.retry = i++; state.sabOP = new SharedArrayBuffer( - i * 4/* ==sizeof int32, noting that Atomics.wait() and friends - can only function on Int32Array views of an SAB. */); - opfsUtil.metrics.reset(); + i * 4/* 4==sizeof int32, noting that Atomics.wait() and + friends can only function on Int32Array views of an + SAB. */); } /** SQLITE_xxx constants to export to the async worker counterpart... */ state.sq3Codes = Object.create(null); - [ + for(const k of [ 'SQLITE_ACCESS_EXISTS', 'SQLITE_ACCESS_READWRITE', 'SQLITE_BUSY', @@ -18291,17 +18643,22 @@ const installOpfsVfs = function callee(options){ 'SQLITE_OPEN_CREATE', 'SQLITE_OPEN_DELETEONCLOSE', 'SQLITE_OPEN_MAIN_DB', - 'SQLITE_OPEN_READONLY' - ].forEach((k)=>{ - if(undefined === (state.sq3Codes[k] = capi[k])){ - toss("Maintenance required: not found:",k); - } - }); + 'SQLITE_OPEN_READONLY', + 'SQLITE_LOCK_NONE', + 'SQLITE_LOCK_SHARED', + 'SQLITE_LOCK_RESERVED', + 'SQLITE_LOCK_PENDING', + 'SQLITE_LOCK_EXCLUSIVE' + ]){ + state.sq3Codes[k] = + capi[k] ?? toss("Maintenance required: not found:",k); + } + state.opfsFlags = Object.assign(Object.create(null),{ /** Flag for use with xOpen(). URI flag "opfs-unlock-asap=1" enables this. See defaultUnlockAsap, below. - */ + */ OPFS_UNLOCK_ASAP: 0x01, /** Flag for use with xOpen(). URI flag "delete-before-open=1" @@ -18314,33 +18671,37 @@ const installOpfsVfs = function callee(options){ downstream errors. An unlink can fail if, e.g., another tab has the handle open. - It goes without saying that deleting a file out from under another - instance results in Undefined Behavior. + It goes without saying that deleting a file out from under + another instance results in Undefined Behavior. */ OPFS_UNLINK_BEFORE_OPEN: 0x02, /** - If true, any async routine which implicitly acquires a sync - access handle (i.e. an OPFS lock) will release that lock at - the end of the call which acquires it. If false, such - "autolocks" are not released until the VFS is idle for some - brief amount of time. - - The benefit of enabling this is much higher concurrency. The - down-side is much-reduced performance (as much as a 4x decrease - in speedtest1). + If true, any async routine which must implicitly acquire a + sync access handle (i.e. an OPFS lock), without an active + xLock(), will release that lock at the end of the call which + acquires it. If false, such implicit locks are not released + until the VFS is idle for some brief amount of time, as + defined by state.asyncIdleWaitTime. + + The benefit of enabling this is higher concurrency. The + down-side is much-reduced performance (as much as a 4x + decrease in speedtest1). */ defaultUnlockAsap: false }); + opfsVfs.metrics.reset()/*must not be called until state.opIds is set up*/; + const metrics = opfsVfs.metrics.counters; + /** Runs the given operation (by name) in the async worker counterpart, waits for its response, and returns the result - which the async worker writes to SAB[state.opIds.rc]. The - 2nd and subsequent arguments must be the arguments for the - async op. + which the async worker writes to SAB[state.opIds.rc]. The 2nd + and subsequent arguments must be the arguments for the async op + (see sqlite3-opfs-async-proxy.c-pp.js). */ - const opRun = (op,...args)=>{ - const opNdx = state.opIds[op] || toss("Invalid op ID:",op); + const opRun = opfsVfs.opRun = (op,...args)=>{ + const opNdx = state.opIds[op] || toss(opfsVfs.vfsName+": Invalid op ID:",op); state.s11n.serialize(...args); Atomics.store(state.sabOPView, state.opIds.rc, -1); Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); @@ -18355,14 +18716,15 @@ const installOpfsVfs = function callee(options){ https://github.com/sqlite/sqlite-wasm/issues/12 Summary: in at least one browser flavor, under high loads, - the wait()/notify() pairings can get out of sync. Calling - wait() here until it returns 'not-equal' gets them back in - sync. + the wait()/notify() pairings can get out of sync and/or + spuriously wake up. Calling wait() here until it returns + 'not-equal' gets them back in sync. */ } /* When the above wait() call returns 'not-equal', the async - half will have completed the operation and reported its results - in the state.opIds.rc slot of the SAB. */ + half will have completed the operation and reported its + results in the state.opIds.rc slot of the SAB. It may have + also serialized an exception for us. */ const rc = Atomics.load(state.sabOPView, state.opIds.rc); metrics[op].wait += performance.now() - t; if(rc && state.asyncS11nExceptions){ @@ -18372,248 +18734,41 @@ const installOpfsVfs = function callee(options){ return rc; }; - /** - Not part of the public API. Only for test/development use. - */ - opfsUtil.debug = { - asyncShutdown: ()=>{ - warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); - opRun('opfs-async-shutdown'); - }, - asyncRestart: ()=>{ - warn("Attempting to restart OPFS VFS async listener. Might work, might not."); - W.postMessage({type: 'opfs-async-restart'}); - } - }; - - const initS11n = ()=>{ - /** - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ACHTUNG: this code is 100% duplicated in the other half of - this proxy! The documentation is maintained in the - "synchronous half". - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - This proxy de/serializes cross-thread function arguments and - output-pointer values via the state.sabIO SharedArrayBuffer, - using the region defined by (state.sabS11nOffset, - state.sabS11nOffset + state.sabS11nSize]. Only one dataset is - recorded at a time. - - This is not a general-purpose format. It only supports the - range of operations, and data sizes, needed by the - sqlite3_vfs and sqlite3_io_methods operations. Serialized - data are transient and this serialization algorithm may - change at any time. - - The data format can be succinctly summarized as: - - Nt...Td...D - - Where: - - - N = number of entries (1 byte) - - - t = type ID of first argument (1 byte) - - - ...T = type IDs of the 2nd and subsequent arguments (1 byte - each). - - - d = raw bytes of first argument (per-type size). - - - ...D = raw bytes of the 2nd and subsequent arguments (per-type - size). - - All types except strings have fixed sizes. Strings are stored - using their TextEncoder/TextDecoder representations. It would - arguably make more sense to store them as Int16Arrays of - their JS character values, but how best/fastest to get that - in and out of string form is an open point. Initial - experimentation with that approach did not gain us any speed. - - Historical note: this impl was initially about 1% this size by - using using JSON.stringify/parse(), but using fit-to-purpose - serialization saves considerable runtime. - */ - if(state.s11n) return state.s11n; - const textDecoder = new TextDecoder(), - textEncoder = new TextEncoder('utf-8'), - viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), - viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - state.s11n = Object.create(null); - /* Only arguments and return values of these types may be - serialized. This covers the whole range of types needed by the - sqlite3_vfs API. */ - const TypeIds = Object.create(null); - TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; - TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; - TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; - TypeIds.string = { id: 4 }; - - const getTypeId = (v)=>( - TypeIds[typeof v] - || toss("Maintenance required: this value type cannot be serialized.",v) - ); - const getTypeIdById = (tid)=>{ - switch(tid){ - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:",tid); - } - }; - - /** - Returns an array of the deserialized state stored by the most - recent serialize() operation (from this thread or the - counterpart thread), or null if the serialization buffer is - empty. If passed a truthy argument, the serialization buffer - is cleared after deserialization. - */ - state.s11n.deserialize = function(clear=false){ - ++metrics.s11n.deserialize.count; - const t = performance.now(); - const argc = viewU8[0]; - const rc = argc ? [] : null; - if(argc){ - const typeIds = []; - let offset = 1, i, n, v; - for(i = 0; i < argc; ++i, ++offset){ - typeIds.push(getTypeIdById(viewU8[offset])); - } - for(i = 0; i < argc; ++i){ - const t = typeIds[i]; - if(t.getter){ - v = viewDV[t.getter](offset, state.littleEndian); - offset += t.size; - }else{/*String*/ - n = viewDV.getInt32(offset, state.littleEndian); - offset += 4; - v = textDecoder.decode(viewU8.slice(offset, offset+n)); - offset += n; - } - rc.push(v); - } - } - if(clear) viewU8[0] = 0; - //log("deserialize:",argc, rc); - metrics.s11n.deserialize.time += performance.now() - t; - return rc; - }; - - /** - Serializes all arguments to the shared buffer for consumption - by the counterpart thread. - - This routine is only intended for serializing OPFS VFS - arguments and (in at least one special case) result values, - and the buffer is sized to be able to comfortably handle - those. - - If passed no arguments then it zeroes out the serialization - state. - */ - state.s11n.serialize = function(...args){ - const t = performance.now(); - ++metrics.s11n.serialize.count; - if(args.length){ - //log("serialize():",args); - const typeIds = []; - let i = 0, offset = 1; - viewU8[0] = args.length & 0xff /* header = # of args */; - for(; i < args.length; ++i, ++offset){ - /* Write the TypeIds.id value into the next args.length - bytes. */ - typeIds.push(getTypeId(args[i])); - viewU8[offset] = typeIds[i].id; - } - for(i = 0; i < args.length; ++i) { - /* Deserialize the following bytes based on their - corresponding TypeIds.id from the header. */ - const t = typeIds[i]; - if(t.setter){ - viewDV[t.setter](offset, args[i], state.littleEndian); - offset += t.size; - }else{/*String*/ - const s = textEncoder.encode(args[i]); - viewDV.setInt32(offset, s.byteLength, state.littleEndian); - offset += 4; - viewU8.set(s, offset); - offset += s.byteLength; - } - } - //log("serialize() result:",viewU8.slice(0,offset)); - }else{ - viewU8[0] = 0; - } - metrics.s11n.serialize.time += performance.now() - t; - }; - return state.s11n; - }/*initS11n()*/; - - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(""); - /* - An alternative impl. with an unpredictable length - but much simpler: - - Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) - */ - }; - - /** - Map of sqlite3_file pointers to objects constructed by xOpen(). - */ - const __openFiles = Object.create(null); - const opTimer = Object.create(null); opTimer.op = undefined; opTimer.start = undefined; - const mTimeStart = (op)=>{ + const mTimeStart = opfsVfs.mTimeStart = (op)=>{ opTimer.start = performance.now(); opTimer.op = op; ++metrics[op].count; }; - const mTimeEnd = ()=>( + const mTimeEnd = opfsVfs.mTimeEnd = ()=>( metrics[opTimer.op].time += performance.now() - opTimer.start ); + /** + Map of sqlite3_file pointers to objects constructed by xOpen(). + */ + const __openFiles = opfsVfs.__openFiles = Object.create(null); + /** Impls for the sqlite3_io_methods methods. Maintenance reminder: members are in alphabetical order to simplify finding them. */ - const ioSyncWrappers = { + const ioSyncWrappers = opfsVfs.ioSyncWrappers = util.nu({ xCheckReservedLock: function(pFile,pOut){ /** - As of late 2022, only a single lock can be held on an OPFS - file. We have no way of checking whether any _other_ db - connection has a lock except by trying to obtain and (on - success) release a sync-handle for it, but doing so would - involve an inherent race condition. For the time being, - pending a better solution, we simply report whether the - given pFile is open. - - Update 2024-06-12: based on forum discussions, this - function now always sets pOut to 0 (false): - - https://sqlite.org/forum/forumpost/a2f573b00cda1372 + After consultation with a topic expert: "opfs-wl" will + continue to use the same no-op impl which "opfs" does + because: + + - xCheckReservedLock() is just a hint. If SQLite needs to + lock, it's still going to try to lock. + + - We cannot do this check synchronously in "opfs-wl", + so would need to pass it to the async proxy. That would + make it inordinately expensive considering that it's + just a hint. */ wasm.poke(pOut, 0, 'i32'); return 0; @@ -18635,7 +18790,7 @@ const installOpfsVfs = function callee(options){ }, xFileControl: function(pFile, opId, pArg){ /*mTimeStart('xFileControl'); - mTimeEnd();*/ + mTimeEnd();*/ return capi.SQLITE_NOTFOUND; }, xFileSize: function(pFile,pSz64){ @@ -18653,25 +18808,8 @@ const installOpfsVfs = function callee(options){ mTimeEnd(); return rc; }, - xLock: function(pFile,lockType){ - mTimeStart('xLock'); - const f = __openFiles[pFile]; - let rc = 0; - /* All OPFS locks are exclusive locks. If xLock() has - previously succeeded, do nothing except record the lock - type. If no lock is active, have the async counterpart - lock the file. */ - if( !f.lockType ) { - rc = opRun('xLock', pFile, lockType); - if( 0===rc ) f.lockType = lockType; - }else{ - f.lockType = lockType; - } - mTimeEnd(); - return rc; - }, xRead: function(pFile,pDest,n,offset64){ - mTimeStart('xRead'); + mTimeStart('xRead'); const f = __openFiles[pFile]; let rc; try { @@ -18693,7 +18831,6 @@ const installOpfsVfs = function callee(options){ }, xSync: function(pFile,flags){ mTimeStart('xSync'); - ++metrics.xSync.count; const rc = opRun('xSync', pFile, flags); mTimeEnd(); return rc; @@ -18704,18 +18841,6 @@ const installOpfsVfs = function callee(options){ mTimeEnd(); return rc; }, - xUnlock: function(pFile,lockType){ - mTimeStart('xUnlock'); - const f = __openFiles[pFile]; - let rc = 0; - if( capi.SQLITE_LOCK_NONE === lockType - && f.lockType ){ - rc = opRun('xUnlock', pFile, lockType); - } - if( 0===rc ) f.lockType = lockType; - mTimeEnd(); - return rc; - }, xWrite: function(pFile,pSrc,n,offset64){ mTimeStart('xWrite'); const f = __openFiles[pFile]; @@ -18732,23 +18857,21 @@ const installOpfsVfs = function callee(options){ mTimeEnd(); return rc; } - }/*ioSyncWrappers*/; + })/*ioSyncWrappers*/; /** Impls for the sqlite3_vfs methods. Maintenance reminder: members are in alphabetical order to simplify finding them. */ - const vfsSyncWrappers = { + const vfsSyncWrappers = opfsVfs.vfsSyncWrappers = { xAccess: function(pVfs,zName,flags,pOut){ - mTimeStart('xAccess'); + mTimeStart('xAccess'); const rc = opRun('xAccess', wasm.cstrToJs(zName)); wasm.poke( pOut, (rc ? 0 : 1), 'i32' ); mTimeEnd(); return 0; }, xCurrentTime: function(pVfs,pOut){ - /* If it turns out that we need to adjust for timezone, see: - https://stackoverflow.com/a/11760121/1458521 */ wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000), 'double'); return 0; @@ -18772,18 +18895,22 @@ const installOpfsVfs = function callee(options){ /*CANTOPEN is required by the docs but SQLITE_RANGE would be a closer match*/; }, xGetLastError: function(pVfs,nOut,pOut){ - /* TODO: store exception.message values from the async - partner in a dedicated SharedArrayBuffer, noting that we'd have - to encode them... TextEncoder can do that for us. */ - warn("OPFS xGetLastError() has nothing sensible to return."); + /* Mutex use in the overlying APIs cause xGetLastError() to + not be terribly useful for us. e.g. it can't be used to + convey error messages from xOpen() because there would be a + race condition between sqlite3_open()'s call to xOpen() and + this function. */ + sqlite3.config.warn("OPFS xGetLastError() has nothing sensible to return."); return 0; }, //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); let opfsFlags = 0; - if(0===zName){ - zName = randomFilename(); + let jzName, zToFree; + if( !zName ){ + jzName = opfsUtil.randomFilename(); + zName = zToFree = wasm.allocCString(jzName); }else if(wasm.isPtr(zName)){ if(capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)){ /* -----------------------^^^^^ MUST pass the untranslated @@ -18793,18 +18920,24 @@ const installOpfsVfs = function callee(options){ if(capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)){ opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; } - zName = wasm.cstrToJs(zName); - //warn("xOpen zName =",zName, "opfsFlags =",opfsFlags); - } - const fh = Object.create(null); - fh.fid = pFile; - fh.filename = zName; - fh.sab = new SharedArrayBuffer(state.fileBufferSize); - fh.flags = flags; - fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) - && !!(flags & capi.SQLITE_OPEN_READONLY); - const rc = opRun('xOpen', pFile, zName, flags, opfsFlags); - if(!rc){ + jzName = wasm.cstrToJs(zName); + //sqlite3.config.warn("xOpen zName =",zName, "opfsFlags =",opfsFlags); + }else{ + sqlite3.config.error("Impossible zName value in xOpen?", zName); + return capi.SQLITE_CANTOPEN; + } + const fh = util.nu({ + fid: pFile, + filename: jzName, + sab: new SharedArrayBuffer(state.fileBufferSize), + flags: flags, + readOnly: !(capi.SQLITE_OPEN_CREATE & flags) + && !!(flags & capi.SQLITE_OPEN_READONLY) + }); + const rc = opRun('xOpen', pFile, jzName, flags, opfsFlags); + if(rc){ + if( zToFree ) wasm.dealloc(zToFree); + }else{ /* Recall that sqlite3_vfs::xClose() will be called, even on error, unless pFile->pMethods is NULL. */ if(fh.readOnly){ @@ -18812,7 +18945,8 @@ const installOpfsVfs = function callee(options){ } __openFiles[pFile] = fh; fh.sabView = state.sabFileBufView; - fh.sq3File = new sqlite3_file(pFile); + fh.sq3File = new capi.sqlite3_file(pFile); + if( zToFree ) fh.sq3File.addOnDispose(zToFree); fh.sq3File.$pMethods = opfsIoMethods.pointer; fh.lockType = capi.SQLITE_LOCK_NONE; } @@ -18821,515 +18955,577 @@ const installOpfsVfs = function callee(options){ }/*xOpen()*/ }/*vfsSyncWrappers*/; - if(dVfs){ - opfsVfs.$xRandomness = dVfs.$xRandomness; - opfsVfs.$xSleep = dVfs.$xSleep; - } - if(!opfsVfs.$xRandomness){ - /* If the default VFS has no xRandomness(), add a basic JS impl... */ - vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for(; i < nOut; ++i) heap[npOut + i] = (Math.random()*255000) & 0xFF; - return i; - }; - } - if(!opfsVfs.$xSleep){ - /* If we can inherit an xSleep() impl from the default VFS then - assume it's sane and use it, otherwise install a JS-based - one. */ - vfsSyncWrappers.xSleep = function(pVfs,ms){ - Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); - return 0; - }; - } + const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; + if(pDVfs){ + const dVfs = new capi.sqlite3_vfs(pDVfs); + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + dVfs.dispose(); + } + if(!opfsVfs.$xRandomness){ + /* If the default VFS has no xRandomness(), add a basic JS impl... */ + opfsVfs.vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for(; i < nOut; ++i) heap[npOut + i] = (Math.random()*255000) & 0xFF; + return i; + }; + } + if(!opfsVfs.$xSleep){ + /* If we can inherit an xSleep() impl from the default VFS then + assume it's sane and use it, otherwise install a JS-based + one. */ + opfsVfs.vfsSyncWrappers.xSleep = function(pVfs,ms){ + mTimeStart('xSleep'); + Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); + mTimeEnd(); + return 0; + }; + } + +const initS11n = function(){ + /** + This proxy de/serializes cross-thread function arguments and + output-pointer values via the state.sabIO SharedArrayBuffer, + using the region defined by (state.sabS11nOffset, + state.sabS11nOffset + state.sabS11nSize]. Only one dataset is + recorded at a time. + + This is not a general-purpose format. It only supports the + range of operations, and data sizes, needed by the + sqlite3_vfs and sqlite3_io_methods operations. Serialized + data are transient and this serialization algorithm may + change at any time. + + The data format can be succinctly summarized as: + + Nt...Td...D + + Where: + + - N = number of entries (1 byte) + + - t = type ID of first argument (1 byte) + + - ...T = type IDs of the 2nd and subsequent arguments (1 byte + each). + + - d = raw bytes of first argument (per-type size). + + - ...D = raw bytes of the 2nd and subsequent arguments (per-type + size). + + All types except strings have fixed sizes. Strings are stored + using their TextEncoder/TextDecoder representations. It would + arguably make more sense to store them as Int16Arrays of + their JS character values, but how best/fastest to get that + in and out of string form is an open point. Initial + experimentation with that approach did not gain us any speed. + + Historical note: this impl was initially about 1% this size by + using using JSON.stringify/parse(), but using fit-to-purpose + serialization saves considerable runtime. + */ + if(state.s11n) return state.s11n; + const textDecoder = new TextDecoder(), + textEncoder = new TextEncoder('utf-8'), + viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), + viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + state.s11n = Object.create(null); + /* Only arguments and return values of these types may be + serialized. This covers the whole range of types needed by the + sqlite3_vfs API. */ + const TypeIds = Object.create(null); + TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; + TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; + TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; + TypeIds.string = { id: 4 }; + + const getTypeId = (v)=>( + TypeIds[typeof v] + || toss("Maintenance required: this value type cannot be serialized.",v) + ); + const getTypeIdById = (tid)=>{ + switch(tid){ + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:",tid); + } + }; + + /** + Returns an array of the deserialized state stored by the most + recent serialize() operation (from this thread or the + counterpart thread), or null if the serialization buffer is + empty. If passed a truthy argument, the serialization buffer + is cleared after deserialization. + */ + state.s11n.deserialize = function(clear=false){ + const t = performance.now(); + const argc = viewU8[0]; + const rc = argc ? [] : null; + if(argc){ + const typeIds = []; + let offset = 1, i, n, v; + for(i = 0; i < argc; ++i, ++offset){ + typeIds.push(getTypeIdById(viewU8[offset])); + } + for(i = 0; i < argc; ++i){ + const t = typeIds[i]; + if(t.getter){ + v = viewDV[t.getter](offset, state.littleEndian); + offset += t.size; + }else{/*String*/ + n = viewDV.getInt32(offset, state.littleEndian); + offset += 4; + v = textDecoder.decode(viewU8.slice(offset, offset+n)); + offset += n; + } + rc.push(v); + } + } + if(clear) viewU8[0] = 0; + //log("deserialize:",argc, rc); + return rc; + }; + + /** + Serializes all arguments to the shared buffer for consumption + by the counterpart thread. + + This routine is only intended for serializing OPFS VFS + arguments and (in at least one special case) result values, + and the buffer is sized to be able to comfortably handle + those. + + If passed no arguments then it zeroes out the serialization + state. + */ + state.s11n.serialize = function(...args){ + const t = performance.now(); + if(args.length){ + //log("serialize():",args); + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 0xff /* header = # of args */; + for(; i < args.length; ++i, ++offset){ + /* Write the TypeIds.id value into the next args.length + bytes. */ + typeIds.push(getTypeId(args[i])); + viewU8[offset] = typeIds[i].id; + } + for(i = 0; i < args.length; ++i) { + /* Deserialize the following bytes based on their + corresponding TypeIds.id from the header. */ + const t = typeIds[i]; + if(t.setter){ + viewDV[t.setter](offset, args[i], state.littleEndian); + offset += t.size; + }else{/*String*/ + const s = textEncoder.encode(args[i]); + viewDV.setInt32(offset, s.byteLength, state.littleEndian); + offset += 4; + viewU8.set(s, offset); + offset += s.byteLength; + } + } + //log("serialize() result:",viewU8.slice(0,offset)); + }else{ + viewU8[0] = 0; + } + }; + + + return state.s11n; +}/*initS11n()*/; + opfsVfs.initS11n = initS11n; + + /** + To be called by the VFS's main installation routine after it has + wired up enough state to provide its overridden io-method impls + (which must be properties of the ioMethods argument). Returns a + Promise which the installation routine must return. callback must + be a function which performs any post-bootstrap touchups, namely + plugging in a sqlite3.oo1 wrapper. It is passed (sqlite3, opfsVfs), + where opfsVfs is the sqlite3_vfs object which was set up by + opfsUtil.createVfsState(). + */ + opfsVfs.bindVfs = function(ioMethods, callback){ + Object.assign(opfsVfs.ioSyncWrappers, ioMethods); + const thePromise = new Promise(function(promiseResolve_, promiseReject_){ + let promiseWasRejected = undefined; + const promiseReject = (err)=>{ + promiseWasRejected = true; + opfsVfs.dispose(); + return promiseReject_(err); + }; + const promiseResolve = ()=>{ + try{ + callback(sqlite3, opfsVfs); + }catch(e){ + return promiseReject(e); + } + promiseWasRejected = false; + return promiseResolve_(sqlite3); + }; + const options = opfsUtil.options; + let proxyUri = options.proxyUri +( + (options.proxyUri.indexOf('?')<0) ? '?' : '&' + )+'vfs='+vfsName; + //sqlite3.config.error("proxyUri",options.proxyUri, (new Error())); + const W = opfsVfs.worker = + (()=>{ + /* _Sigh_... */ + switch(vfsName){ + case 'opfs': + return new Worker(new URL("sqlite3-opfs-async-proxy.js?vfs=opfs", import.meta.url)); + case 'opfs-wl': + return new Worker(new URL("sqlite3-opfs-async-proxy.js?vfs=opfs-wl", import.meta.url)); + } + })(); + let zombieTimer = setTimeout(()=>{ + /* At attempt to work around a browser-specific quirk in which + the Worker load is failing in such a way that we neither + resolve nor reject it. This workaround gives that resolve/reject + a time limit and rejects if that timer expires. Discussion: + https://sqlite.org/forum/forumpost/a708c98dcb3ef */ + if(undefined===promiseWasRejected){ + promiseReject( + new Error("Timeout while waiting for OPFS async proxy worker.") + ); + } + }, 4000); + W._originalOnError = W.onerror /* will be restored later */; + W.onerror = function(err){ + // The error object doesn't contain any useful info when the + // failure is, e.g., that the remote script is 404. + error("Error initializing OPFS asyncer:",err); + promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); + }; + + const opRun = opfsVfs.opRun; + + const sanityCheck = function(){ + const scope = wasm.scopedAllocPush(); + const sq3File = new capi.sqlite3_file(); + try{ + const fid = sq3File.pointer; + const openFlags = capi.SQLITE_OPEN_CREATE + | capi.SQLITE_OPEN_READWRITE + //| capi.SQLITE_OPEN_DELETEONCLOSE + | capi.SQLITE_OPEN_MAIN_DB; + const pOut = wasm.scopedAlloc(8); + const dbFile = "/sanity/check/file"+randomFilename(8); + const zDbFile = wasm.scopedAllocCString(dbFile); + let rc; + state.s11n.serialize("This is ä string."); + rc = state.s11n.deserialize(); + log("deserialize() says:",rc); + if("This is ä string."!==rc[0]) toss("String d13n error."); + opfsVfs.vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut,'i32'); + log("xAccess(",dbFile,") exists ?=",rc); + rc = opfsVfs.vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, + fid, openFlags, pOut); + log("open rc =",rc,"state.sabOPView[xOpen] =", + state.sabOPView[state.opIds.xOpen]); + if(0!==rc){ + error("open failed with code",rc); + return; + } + opfsVfs.vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut,'i32'); + if(!rc) toss("xAccess() failed to detect file."); + rc = opfsVfs.ioSyncWrappers.xSync(sq3File.pointer, 0); + if(rc) toss('sync failed w/ rc',rc); + rc = opfsVfs.ioSyncWrappers.xTruncate(sq3File.pointer, 1024); + if(rc) toss('truncate failed w/ rc',rc); + wasm.poke(pOut,0,'i64'); + rc = opfsVfs.ioSyncWrappers.xFileSize(sq3File.pointer, pOut); + if(rc) toss('xFileSize failed w/ rc',rc); + log("xFileSize says:",wasm.peek(pOut, 'i64')); + rc = opfsVfs.ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); + if(rc) toss("xWrite() failed!"); + const readBuf = wasm.scopedAlloc(16); + rc = opfsVfs.ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); + wasm.poke(readBuf+6,0); + let jRead = wasm.cstrToJs(readBuf); + log("xRead() got:",jRead); + if("sanity"!==jRead) toss("Unexpected xRead() value."); + if(opfsVfs.vfsSyncWrappers.xSleep){ + log("xSleep()ing before close()ing..."); + opfsVfs.vfsSyncWrappers.xSleep(opfsVfs.pointer,2000); + log("waking up from xSleep()"); + } + rc = opfsVfs.ioSyncWrappers.xClose(fid); + log("xClose rc =",rc,"sabOPView =",state.sabOPView); + log("Deleting file:",dbFile); + opfsVfs.vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); + opfsVfs.vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut,'i32'); + if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); + warn("End of OPFS sanity checks."); + }finally{ + sq3File.dispose(); + wasm.scopedAllocPop(scope); + } + }/*sanityCheck()*/; + + W.onmessage = function({data}){ + //sqlite3.config.warn(vfsName,"Worker.onmessage:",data); + switch(data.type){ + case 'opfs-unavailable': + /* Async proxy has determined that OPFS is unavailable. There's + nothing more for us to do here. */ + promiseReject(new Error(data.payload.join(' '))); + break; + case 'opfs-async-loaded': + /* Arrives as soon as the asyc proxy finishes loading. + Pass our config and shared state on to the async + worker. */ + delete state.vfs; + W.postMessage({type: 'opfs-async-init', args: util.nu(state)}); + break; + case 'opfs-async-inited': { + /* Indicates that the async partner has received the 'init' + and has finished initializing, so the real work can + begin... */ + if(true===promiseWasRejected){ + break /* promise was already rejected via timer */; + } + clearTimeout(zombieTimer); + zombieTimer = null; + try { + sqlite3.vfs.installVfs({ + io: {struct: opfsVfs.ioMethods, methods: opfsVfs.ioSyncWrappers}, + vfs: {struct: opfsVfs, methods: opfsVfs.vfsSyncWrappers} + }); + state.sabOPView = new Int32Array(state.sabOP); + state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); + state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + opfsVfs.initS11n(); + delete opfsVfs.initS11n; + if(options.sanityChecks){ + warn("Running sanity checks because of opfs-sanity-check URL arg..."); + sanityCheck(); + } + if(opfsUtil.thisThreadHasOPFS()){ + opfsUtil.getRootDir().then((d)=>{ + W.onerror = W._originalOnError; + delete W._originalOnError; + log("End of OPFS sqlite3_vfs setup.", opfsVfs); + promiseResolve(); + }).catch(promiseReject); + }else{ + promiseResolve(); + } + }catch(e){ + error(e); + promiseReject(e); + } + break; + } + case 'debug': + warn("debug message from worker:",data); + break; + default: { + const errMsg = ( + "Unexpected message from the OPFS async worker: " + + JSON.stringify(data) + ); + error(errMsg); + promiseReject(new Error(errMsg)); + break; + } + }/*switch(data.type)*/ + }/*W.onmessage()*/; + })/*thePromise*/; + return thePromise; + }/*bindVfs()*/; + + return state; + }/*createVfsState()*/; + +}/*sqlite3ApiBootstrap.initializers*/); +/* + 2022-09-18 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This file holds the synchronous half of an sqlite3_vfs + implementation which proxies, in a synchronous fashion, the + asynchronous Origin-Private FileSystem (OPFS) APIs using a second + Worker, implemented in sqlite3-opfs-async-proxy.js. This file is + intended to be appended to the main sqlite3 JS deliverable somewhere + after sqlite3-api-oo1.js. +*/ +'use strict'; +globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ + if( !sqlite3.opfs || sqlite3.config.disable?.vfs?.opfs ){ + return; + } + const util = sqlite3.util, + opfsUtil = sqlite3.opfs || sqlite3.util.toss("Missing sqlite3.opfs"); + /** + installOpfsVfs() returns a Promise which, on success, installs an + sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs + which accept a VFS. It is intended to be called via + sqlite3ApiBootstrap.initializers or an equivalent mechanism. + + The installed VFS uses the Origin-Private FileSystem API for + all file storage. On error it is rejected with an exception + explaining the problem. Reasons for rejection include, but are + not limited to: - /** - Expects an OPFS file path. It gets resolved, such that ".." - components are properly expanded, and returned. If the 2nd arg - is true, the result is returned as an array of path elements, - else an absolute path string is returned. - */ - opfsUtil.getResolvedPath = function(filename,splitIt){ - const p = new URL(filename, "file://irrelevant").pathname; - return splitIt ? p.split('/').filter((v)=>!!v) : p; - }; + - The counterpart Worker (see below) could not be loaded. - /** - Takes the absolute path to a filesystem element. Returns an - array of [handleOfContainingDir, filename]. If the 2nd argument - is truthy then each directory element leading to the file is - created along the way. Throws if any creation or resolution - fails. - */ - opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false){ - const path = opfsUtil.getResolvedPath(absFilename, true); - const filename = path.pop(); - let dh = opfsUtil.rootDirectory; - for(const dirName of path){ - if(dirName){ - dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); - } - } - return [dh, filename]; - }; + - The environment does not support OPFS. That includes when + this function is called from the main window thread. - /** - Creates the given directory name, recursively, in - the OPFS filesystem. Returns true if it succeeds or the - directory already exists, else false. - */ - opfsUtil.mkdir = async function(absDirName){ - try { - await opfsUtil.getDirForFilename(absDirName+"/filepart", true); - return true; - }catch(e){ - //sqlite3.config.warn("mkdir(",absDirName,") failed:",e); - return false; - } - }; - /** - Checks whether the given OPFS filesystem entry exists, - returning true if it does, false if it doesn't or if an - exception is intercepted while trying to make the - determination. - */ - opfsUtil.entryExists = async function(fsEntryName){ - try { - const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); - await dh.getFileHandle(fn); - return true; - }catch(e){ - return false; - } - }; + Significant notes and limitations: - /** - Generates a random ASCII string, intended for use as a - temporary file name. Its argument is the length of the string, - defaulting to 16. - */ - opfsUtil.randomFilename = randomFilename; + - The OPFS features used here are only available in dedicated Worker + threads. This file tries to detect that case, resulting in a + rejected Promise if those features do not seem to be available. - /** - Returns a promise which resolves to an object which represents - all files and directories in the OPFS tree. The top-most object - has two properties: `dirs` is an array of directory entries - (described below) and `files` is a list of file names for all - files in that directory. + - It requires the SharedArrayBuffer and Atomics classes, and the + former is only available if the HTTP server emits the so-called + COOP and COEP response headers. These features are required for + proxying OPFS's synchronous API via the synchronous interface + required by the sqlite3_vfs API. - Traversal starts at sqlite3.opfs.rootDirectory. + - This function may only be called a single time. When called, this + function removes itself from the sqlite3 object. - Each `dirs` entry is an object in this form: + All arguments to this function are for internal/development purposes + only. They do not constitute a public API and may change at any + time. - ``` - { name: directoryName, - dirs: [...subdirs], - files: [...file names] - } - ``` + The argument may optionally be a plain object with the following + configuration options: - The `files` and `subdirs` entries are always set but may be - empty arrays. + - proxyUri: name of the async proxy JS file or a synchronous function + which, when called, returns such a name. - The returned object has the same structure but its `name` is - an empty string. All returned objects are created with - Object.create(null), so have no prototype. + - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables + logging of errors. 2 enables logging of warnings and errors. 3 + additionally enables debugging info. Logging is performed + via the sqlite3.config.{log|warn|error}() functions. - Design note: the entries do not contain more information, - e.g. file sizes, because getting such info is not only - expensive but is subject to locking-related errors. - */ - opfsUtil.treeList = async function(){ - const doDir = async function callee(dirHandle,tgt){ - tgt.name = dirHandle.name; - tgt.dirs = []; - tgt.files = []; - for await (const handle of dirHandle.values()){ - if('directory' === handle.kind){ - const subDir = Object.create(null); - tgt.dirs.push(subDir); - await callee(handle, subDir); - }else{ - tgt.files.push(handle.name); - } - } - }; - const root = Object.create(null); - await doDir(opfsUtil.rootDirectory, root); - return root; - }; + - sanityChecks (=false): if true, some basic sanity tests are run on + the OPFS VFS API after it's initialized, before the returned + Promise resolves. This is only intended for testing and + development of the VFS, not client-side use. - /** - Irrevocably deletes _all_ files in the current origin's OPFS. - Obviously, this must be used with great caution. It may throw - an exception if removal of anything fails (e.g. a file is - locked), but the precise conditions under which the underlying - APIs will throw are not documented (so we cannot tell you what - they are). - */ - opfsUtil.rmfr = async function(){ - const dir = opfsUtil.rootDirectory, opt = {recurse: true}; - for await (const handle of dir.values()){ - dir.removeEntry(handle.name, opt); - } - }; + Additionaly, the (officially undocumented) 'opfs-disable' URL + argument will disable OPFS, making this function a no-op. - /** - Deletes the given OPFS filesystem entry. As this environment - has no notion of "current directory", the given name must be an - absolute path. If the 2nd argument is truthy, deletion is - recursive (use with caution!). - - The returned Promise resolves to true if the deletion was - successful, else false (but...). The OPFS API reports the - reason for the failure only in human-readable form, not - exceptions which can be type-checked to determine the - failure. Because of that... - - If the final argument is truthy then this function will - propagate any exception on error, rather than returning false. - */ - opfsUtil.unlink = async function(fsEntryName, recursive = false, - throwOnError = false){ - try { - const [hDir, filenamePart] = - await opfsUtil.getDirForFilename(fsEntryName, false); - await hDir.removeEntry(filenamePart, {recursive}); - return true; - }catch(e){ - if(throwOnError){ - throw new Error("unlink(",arguments[0],") failed: "+e.message,{ - cause: e - }); - } - return false; - } - }; + On success, the Promise resolves to the top-most sqlite3 namespace + object. Success does not necessarily mean that it installs the VFS, + as there are legitimate non-error reasons for OPFS not to be + available. +*/ +const installOpfsVfs = async function(options){ + options = opfsUtil.initOptions('opfs',options); + if( !options ) return sqlite3; + const capi = sqlite3.capi, + state = opfsUtil.createVfsState(), + opfsVfs = state.vfs, + metrics = opfsVfs.metrics.counters, + mTimeStart = opfsVfs.mTimeStart, + mTimeEnd = opfsVfs.mTimeEnd, + opRun = opfsVfs.opRun, + debug = (...args)=>sqlite3.config.debug("opfs:",...args), + warn = (...args)=>sqlite3.config.warn("opfs:",...args), + __openFiles = opfsVfs.__openFiles; + + //debug("options:",JSON.stringify(options)); + /* + At this point, createVfsState() has populated: - /** - Traverses the OPFS filesystem, calling a callback for each - entry. The argument may be either a callback function or an - options object with any of the following properties: - - - `callback`: function which gets called for each filesystem - entry. It gets passed 3 arguments: 1) the - FileSystemFileHandle or FileSystemDirectoryHandle of each - entry (noting that both are instanceof FileSystemHandle). 2) - the FileSystemDirectoryHandle of the parent directory. 3) the - current depth level, with 0 being at the top of the tree - relative to the starting directory. If the callback returns a - literal false, as opposed to any other falsy value, traversal - stops without an error. Any exceptions it throws are - propagated. Results are undefined if the callback manipulate - the filesystem (e.g. removing or adding entries) because the - how OPFS iterators behave in the face of such changes is - undocumented. - - - `recursive` [bool=true]: specifies whether to recurse into - subdirectories or not. Whether recursion is depth-first or - breadth-first is unspecified! - - - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] - specifies the starting directory. - - If this function is passed a function, it is assumed to be the - callback. - - Returns a promise because it has to (by virtue of being async) - but that promise has no specific meaning: the traversal it - performs is synchronous. The promise must be used to catch any - exceptions propagated by the callback, however. - */ - opfsUtil.traverse = async function(opt){ - const defaultOpt = { - recursive: true, - directory: opfsUtil.rootDirectory - }; - if('function'===typeof opt){ - opt = {callback:opt}; - } - opt = Object.assign(defaultOpt, opt||{}); - const doDir = async function callee(dirHandle, depth){ - for await (const handle of dirHandle.values()){ - if(false === opt.callback(handle, dirHandle, depth)) return false; - else if(opt.recursive && 'directory' === handle.kind){ - if(false === await callee(handle, depth + 1)) break; - } - } - }; - doDir(opt.directory, 0); - }; + - state: the configuration object we share with the async proxy. - /** - impl of importDb() when it's given a function as its second - argument. - */ - const importDbChunked = async function(filename, callback){ - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - const hFile = await hDir.getFileHandle(fnamePart, {create:true}); - let sah = await hFile.createSyncAccessHandle(); - let nWrote = 0, chunk, checkedHeader = false, err = false; - try{ - sah.truncate(0); - while( undefined !== (chunk = await callback()) ){ - if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); - if( !checkedHeader && 0===nWrote && chunk.byteLength>=15 ){ - util.affirmDbHeader(chunk); - checkedHeader = true; - } - sah.write(chunk, {at: nWrote}); - nWrote += chunk.byteLength; - } - if( nWrote < 512 || 0!==nWrote % 512 ){ - toss("Input size",nWrote,"is not correct for an SQLite database."); - } - if( !checkedHeader ){ - const header = new Uint8Array(20); - sah.read( header, {at: 0} ); - util.affirmDbHeader( header ); - } - sah.write(new Uint8Array([1,1]), {at: 18}/*force db out of WAL mode*/); - return nWrote; - }catch(e){ - await sah.close(); - sah = undefined; - await hDir.removeEntry( fnamePart ).catch(()=>{}); - throw e; - }finally { - if( sah ) await sah.close(); - } - }; + - opfsVfs: an sqlite3_vfs instance with lots of JS state attached + to it. - /** - Asynchronously imports the given bytes (a byte array or - ArrayBuffer) into the given database file. - - Results are undefined if the given db name refers to an opened - db. - - If passed a function for its second argument, its behaviour - changes: imports its data in chunks fed to it by the given - callback function. It calls the callback (which may be async) - repeatedly, expecting either a Uint8Array or ArrayBuffer (to - denote new input) or undefined (to denote EOF). For so long as - the callback continues to return non-undefined, it will append - incoming data to the given VFS-hosted database file. When - called this way, the resolved value of the returned Promise is - the number of bytes written to the target file. - - It very specifically requires the input to be an SQLite3 - database and throws if that's not the case. It does so in - order to prevent this function from taking on a larger scope - than it is specifically intended to. i.e. we do not want it to - become a convenience for importing arbitrary files into OPFS. - - This routine rewrites the database header bytes in the output - file (not the input array) to force disabling of WAL mode. - - On error this throws and the state of the input file is - undefined (it depends on where the exception was triggered). - - On success, resolves to the number of bytes written. - */ - opfsUtil.importDb = async function(filename, bytes){ - if( bytes instanceof Function ){ - return importDbChunked(filename, bytes); + with any code common to both the "opfs" and "opfs-wl" VFSes. Now + comes the VFS-dependent work... + */ + return opfsVfs.bindVfs(util.nu({ + xLock: function(pFile,lockType){ + mTimeStart('xLock'); + ++metrics.xLock.count; + const f = __openFiles[pFile]; + let rc = 0; + /* All OPFS locks are exclusive locks. If xLock() has + previously succeeded, do nothing except record the lock + type. If no lock is active, have the async counterpart + lock the file. */ + if( f.lockType ) { + f.lockType = lockType; + }else{ + rc = opRun('xLock', pFile, lockType); + if( 0===rc ) f.lockType = lockType; } - if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - util.affirmIsDb(bytes); - const n = bytes.byteLength; - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - let sah, err, nWrote = 0; - try { - const hFile = await hDir.getFileHandle(fnamePart, {create:true}); - sah = await hFile.createSyncAccessHandle(); - sah.truncate(0); - nWrote = sah.write(bytes, {at: 0}); - if(nWrote != n){ - toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); - } - sah.write(new Uint8Array([1,1]), {at: 18}) /* force db out of WAL mode */; - return nWrote; - }catch(e){ - if( sah ){ await sah.close(); sah = undefined; } - await hDir.removeEntry( fnamePart ).catch(()=>{}); - throw e; - }finally{ - if( sah ) await sah.close(); + mTimeEnd(); + return rc; + }, + xUnlock: function(pFile,lockType){ + mTimeStart('xUnlock'); + ++metrics.xUnlock.count; + const f = __openFiles[pFile]; + let rc = 0; + if( capi.SQLITE_LOCK_NONE === lockType + && f.lockType ){ + rc = opRun('xUnlock', pFile, lockType); } - }; - + if( 0===rc ) f.lockType = lockType; + mTimeEnd(); + return rc; + } + }), function(sqlite3, vfs){ + /* Post-VFS-registration initialization... */ if(sqlite3.oo1){ const OpfsDb = function(...args){ const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); - opt.vfs = opfsVfs.$zName; + opt.vfs = vfs.$zName; sqlite3.oo1.DB.dbCtorHelper.call(this, opt); }; OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); sqlite3.oo1.OpfsDb = OpfsDb; OpfsDb.importDb = opfsUtil.importDb; - sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback( - opfsVfs.pointer, - function(oo1Db, sqlite3){ - /* Set a relatively high default busy-timeout handler to - help OPFS dbs deal with multi-tab/multi-worker - contention. */ - sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); - } - ); - }/*extend sqlite3.oo1*/ - - const sanityCheck = function(){ - const scope = wasm.scopedAllocPush(); - const sq3File = new sqlite3_file(); - try{ - const fid = sq3File.pointer; - const openFlags = capi.SQLITE_OPEN_CREATE - | capi.SQLITE_OPEN_READWRITE - //| capi.SQLITE_OPEN_DELETEONCLOSE - | capi.SQLITE_OPEN_MAIN_DB; - const pOut = wasm.scopedAlloc(8); - const dbFile = "/sanity/check/file"+randomFilename(8); - const zDbFile = wasm.scopedAllocCString(dbFile); - let rc; - state.s11n.serialize("This is ä string."); - rc = state.s11n.deserialize(); - log("deserialize() says:",rc); - if("This is ä string."!==rc[0]) toss("String d13n error."); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut,'i32'); - log("xAccess(",dbFile,") exists ?=",rc); - rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, - fid, openFlags, pOut); - log("open rc =",rc,"state.sabOPView[xOpen] =", - state.sabOPView[state.opIds.xOpen]); - if(0!==rc){ - error("open failed with code",rc); - return; - } - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut,'i32'); - if(!rc) toss("xAccess() failed to detect file."); - rc = ioSyncWrappers.xSync(sq3File.pointer, 0); - if(rc) toss('sync failed w/ rc',rc); - rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); - if(rc) toss('truncate failed w/ rc',rc); - wasm.poke(pOut,0,'i64'); - rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); - if(rc) toss('xFileSize failed w/ rc',rc); - log("xFileSize says:",wasm.peek(pOut, 'i64')); - rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); - if(rc) toss("xWrite() failed!"); - const readBuf = wasm.scopedAlloc(16); - rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); - wasm.poke(readBuf+6,0); - let jRead = wasm.cstrToJs(readBuf); - log("xRead() got:",jRead); - if("sanity"!==jRead) toss("Unexpected xRead() value."); - if(vfsSyncWrappers.xSleep){ - log("xSleep()ing before close()ing..."); - vfsSyncWrappers.xSleep(opfsVfs.pointer,2000); - log("waking up from xSleep()"); - } - rc = ioSyncWrappers.xClose(fid); - log("xClose rc =",rc,"sabOPView =",state.sabOPView); - log("Deleting file:",dbFile); - vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut,'i32'); - if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); - warn("End of OPFS sanity checks."); - }finally{ - sq3File.dispose(); - wasm.scopedAllocPop(scope); - } - }/*sanityCheck()*/; - - W.onmessage = function({data}){ - //log("Worker.onmessage:",data); - switch(data.type){ - case 'opfs-unavailable': - /* Async proxy has determined that OPFS is unavailable. There's - nothing more for us to do here. */ - promiseReject(new Error(data.payload.join(' '))); - break; - case 'opfs-async-loaded': - /* Arrives as soon as the asyc proxy finishes loading. - Pass our config and shared state on to the async - worker. */ - W.postMessage({type: 'opfs-async-init',args: state}); - break; - case 'opfs-async-inited': { - /* Indicates that the async partner has received the 'init' - and has finished initializing, so the real work can - begin... */ - if(true===promiseWasRejected){ - break /* promise was already rejected via timer */; - } - try { - sqlite3.vfs.installVfs({ - io: {struct: opfsIoMethods, methods: ioSyncWrappers}, - vfs: {struct: opfsVfs, methods: vfsSyncWrappers} - }); - state.sabOPView = new Int32Array(state.sabOP); - state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); - state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - initS11n(); - if(options.sanityChecks){ - warn("Running sanity checks because of opfs-sanity-check URL arg..."); - sanityCheck(); - } - if(thisThreadHasOPFS()){ - navigator.storage.getDirectory().then((d)=>{ - W.onerror = W._originalOnError; - delete W._originalOnError; - sqlite3.opfs = opfsUtil; - opfsUtil.rootDirectory = d; - log("End of OPFS sqlite3_vfs setup.", opfsVfs); - promiseResolve(); - }).catch(promiseReject); - }else{ - promiseResolve(); - } - }catch(e){ - error(e); - promiseReject(e); - } - break; + if( true ){ + /* 2026-03-06: this was a design mis-decision and is + inconsistent with sqlite3_open() and friends, but is + retained against the risk of introducing regressions if + it's removed. */ + sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback( + opfsVfs.pointer, + function(oo1Db, sqlite3){ + /* Set a relatively high default busy-timeout handler to + help OPFS dbs deal with multi-tab/multi-worker + contention. */ + sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); } - default: { - const errMsg = ( - "Unexpected message from the OPFS async worker: " + - JSON.stringify(data) - ); - error(errMsg); - promiseReject(new Error(errMsg)); - break; - } - }/*switch(data.type)*/ - }/*W.onmessage()*/; - })/*thePromise*/; - return thePromise; + ); + } + }/*extend sqlite3.oo1*/ + })/*bindVfs()*/; }/*installOpfsVfs()*/; -installOpfsVfs.defaultProxyUri = - "sqlite3-opfs-async-proxy.js"; globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ - try{ - let proxyJs = installOpfsVfs.defaultProxyUri; - if( sqlite3?.scriptInfo?.sqlite3Dir ){ - installOpfsVfs.defaultProxyUri = - sqlite3.scriptInfo.sqlite3Dir + proxyJs; - //sqlite3.config.warn("installOpfsVfs.defaultProxyUri =",installOpfsVfs.defaultProxyUri); - } - return installOpfsVfs().catch((e)=>{ - sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:",e.message); - }); - }catch(e){ - sqlite3.config.error("installOpfsVfs() exception:",e); - return Promise.reject(e); - } + return installOpfsVfs().catch((e)=>{ + sqlite3.config.warn("Ignoring inability to install 'opfs' sqlite3_vfs:",e); + }) }); }/*sqlite3ApiBootstrap.initializers.push()*/); /* @@ -19388,6 +19584,10 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'use strict'; + if( sqlite3.config.disable?.vfs?.['opfs-sahpool'] ){ + return; + } + const toss = sqlite3.util.toss; const toss3 = sqlite3.util.toss3; const initPromises = Object.create(null) /* cache of (name:result) of VFS init results */; @@ -20795,6 +20995,132 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }); }/*installOpfsSAHPoolVfs()*/; }/*sqlite3ApiBootstrap.initializers*/); +/* + 2026-02-20 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This file is a reimplementation of the "opfs" VFS (as distinct from + "opfs-sahpool") which uses WebLocks for locking instead of a bespoke + Atomics.wait()/notify() protocol. This file holds the "synchronous + half" of the VFS, whereas it shares the "asynchronous half" with the + "opfs" VFS. + + Testing has failed to show any genuine functional difference between + these VFSes other than "opfs-wl" being able to dole out xLock() + requests in a strictly FIFO manner by virtue of WebLocks being + globally managed by the browser. This tends to lead to, but does not + guaranty, fairer distribution of locks. Differences are unlikely to + be noticed except, perhaps, under very high contention. + + This file is intended to be appended to the main sqlite3 JS + deliverable somewhere after opfs-common-shared.c-pp.js. +*/ +'use strict'; +globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ + if( !sqlite3.opfs || sqlite3.config.disable?.vfs?.['opfs-wl'] ){ + return; + } + const util = sqlite3.util, + toss = sqlite3.util.toss; + const opfsUtil = sqlite3.opfs; + const vfsName = 'opfs-wl'; +/** + installOpfsWlVfs() returns a Promise which, on success, installs an + sqlite3_vfs named "opfs-wl", suitable for use with all sqlite3 APIs + which accept a VFS. It is intended to be called via + sqlite3ApiBootstrap.initializers or an equivalent mechanism. + + This VFS is essentially identical to the "opfs" VFS but uses + WebLocks for its xLock() and xUnlock() implementations. + + Quirks specific to this VFS: + + - The (officially undocumented) 'opfs-wl-disable' URL + argument will disable OPFS, making this function a no-op. + + Aside from locking differences in the VFSes, this function + otherwise behaves the same as + sqlite3-vfs-opfs.c-pp.js:installOpfsVfs(). +*/ +const installOpfsWlVfs = async function(options){ + options = opfsUtil.initOptions(vfsName,options); + if( !options ) return sqlite3; + const capi = sqlite3.capi, + state = opfsUtil.createVfsState(), + opfsVfs = state.vfs, + metrics = opfsVfs.metrics.counters, + mTimeStart = opfsVfs.mTimeStart, + mTimeEnd = opfsVfs.mTimeEnd, + opRun = opfsVfs.opRun, + debug = (...args)=>sqlite3.config.debug(vfsName+":",...args), + warn = (...args)=>sqlite3.config.warn(vfsName+":",...args), + __openFiles = opfsVfs.__openFiles; + + //debug("state",JSON.stringify(options)); + /* + At this point, createVfsState() has populated: + + - state: the configuration object we share with the async proxy. + + - opfsVfs: an sqlite3_vfs instance with lots of JS state attached + to it. + + with any code common to both the "opfs" and "opfs-wl" VFSes. Now + comes the VFS-dependent work... + */ + return opfsVfs.bindVfs(util.nu({ + xLock: function(pFile,lockType){ + mTimeStart('xLock'); + //debug("xLock()..."); + const f = __openFiles[pFile]; + const rc = opRun('xLock', pFile, lockType); + if( !rc ) f.lockType = lockType; + mTimeEnd(); + return rc; + }, + xUnlock: function(pFile,lockType){ + mTimeStart('xUnlock'); + const f = __openFiles[pFile]; + const rc = opRun('xUnlock', pFile, lockType); + if( !rc ) f.lockType = lockType; + mTimeEnd(); + return rc; + } + }), function(sqlite3, vfs){ + /* Post-VFS-registration initialization... */ + if(sqlite3.oo1){ + const OpfsWlDb = function(...args){ + const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); + opt.vfs = vfs.$zName; + sqlite3.oo1.DB.dbCtorHelper.call(this, opt); + }; + OpfsWlDb.prototype = Object.create(sqlite3.oo1.DB.prototype); + sqlite3.oo1.OpfsWlDb = OpfsWlDb; + OpfsWlDb.importDb = opfsUtil.importDb; + /* The "opfs" VFS variant adds a + oo1.DB.dbCtorHelper.setVfsPostOpenCallback() callback to set + a high busy_timeout. That was a design mis-decision and is + inconsistent with sqlite3_open() and friends, but is retained + against the risk of introducing regressions if it's removed. + This variant does not repeat that mistake. + */ + } + })/*bindVfs()*/; +}/*installOpfsWlVfs()*/; +globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ + return installOpfsWlVfs().catch((e)=>{ + sqlite3.config.warn("Ignoring inability to install the",vfsName,"sqlite3_vfs:",e); + }); +}); +}/*sqlite3ApiBootstrap.initializers.push()*/); /* 2022-07-22 diff --git a/src/bin/sqlite3-node.mjs b/src/bin/sqlite3-node.mjs index ddcfbc1..09e55f4 100644 --- a/src/bin/sqlite3-node.mjs +++ b/src/bin/sqlite3-node.mjs @@ -27,11 +27,11 @@ /* @preserve ** This code was built from sqlite3 version... ** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** SQLITE_VERSION "3.53.0" +** SQLITE_VERSION_NUMBER 3053000 +** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** -** Emscripten SDK: 5.0.0 +** Emscripten SDK: 5.0.5 */ // This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. @@ -98,7 +98,7 @@ if (ENVIRONMENT_IS_NODE) { /** This file was preprocessed using: - ./c-pp-lite -o ./bld/pre-js.node.js -Dtarget:node -Dtarget:es6-module -Dtarget:es6-bundler-friendly -Dunsupported-build -DModule.instantiateWasm api/pre-js.c-pp.js + ./c-pp -o ./bld/pre-js.node.js -Dtarget:node -Dtarget:es6-module -Dtarget:es6-bundler-friendly -Dunsupported-build -DModule.instantiateWasm api/pre-js.c-pp.js */ /** UNSUPPORTED BUILD: @@ -108,7 +108,8 @@ if (ENVIRONMENT_IS_NODE) { load. It may not work properly. Only builds _directly_ targeting browser environments ("vanilla" JS and ESM modules) are supported and tested. Builds which _indirectly_ target browsers (namely - bundler-friendly builds) are not supported deliverables. + bundler-friendly builds and any node builds) are not supported + deliverables. */ /* END FILE: api/pre-js.js. */ // end include: ./bld/pre-js.node.js @@ -235,37 +236,17 @@ var isFileURI = (filename) => filename.startsWith('file://'); // include: runtime_stack_check.js // end include: runtime_stack_check.js // include: runtime_exceptions.js +// Base Emscripten EH error class +class EmscriptenEH {} + +class EmscriptenSjLj extends EmscriptenEH {} + // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js var readyPromiseResolve, readyPromiseReject; // Memory management -var -/** @type {!Int8Array} */ - HEAP8, -/** @type {!Uint8Array} */ - HEAPU8, -/** @type {!Int16Array} */ - HEAP16, -/** @type {!Uint16Array} */ - HEAPU16, -/** @type {!Int32Array} */ - HEAP32, -/** @type {!Uint32Array} */ - HEAPU32, -/** @type {!Float32Array} */ - HEAPF32, -/** @type {!Float64Array} */ - HEAPF64; - -// BigInt64Array type is not correctly defined in closure -var -/** not-@type {!BigInt64Array} */ - HEAP64, -/* BigUint64Array type is not correctly defined in closure -/** not-@type {!BigUint64Array} */ - HEAPU64; var runtimeInitialized = false; @@ -363,11 +344,14 @@ function postRun() { // End ATPOSTRUNS hooks } -/** @param {string|number=} what */ +/** + * @param {string|number=} what + * @noreturn + */ function abort(what) { Module['onAbort']?.(what); - what = 'Aborted(' + what + ')'; + what = `Aborted(${what})`; // TODO(sbc): Should we remove printing and leave it up to whoever // catches the exception? err(what); @@ -545,6 +529,36 @@ async function createWasm() { } } + /** @type {!Int16Array} */ + var HEAP16; + + /** @type {!Int32Array} */ + var HEAP32; + + /** not-@type {!BigInt64Array} */ + var HEAP64; + + /** @type {!Int8Array} */ + var HEAP8; + + /** @type {!Float32Array} */ + var HEAPF32; + + /** @type {!Float64Array} */ + var HEAPF64; + + /** @type {!Uint16Array} */ + var HEAPU16; + + /** @type {!Uint32Array} */ + var HEAPU32; + + /** not-@type {!BigUint64Array} */ + var HEAPU64; + + /** @type {!Uint8Array} */ + var HEAPU8; + var callRuntimeCallbacks = (callbacks) => { while (callbacks.length > 0) { // Pass the module as the first argument. @@ -675,12 +689,9 @@ var initRandomFill = () => { return (view) => nodeCrypto.randomFillSync(view); } - return (view) => crypto.getRandomValues(view); - }; -var randomFill = (view) => { - // Lazily init on the first invocation. - (randomFill = initRandomFill())(view); + return (view) => (crypto.getRandomValues(view), 0); }; +var randomFill = (view) => (randomFill = initRandomFill())(view); @@ -1098,11 +1109,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node; node.stream_ops = MEMFS.ops_table.file.stream; - node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. - // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred - // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size - // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. - node.contents = null; + // The actual number of bytes used in the typed array, as opposed to + // contents.length which gives the whole capacity. + node.usedBytes = 0; + // The byte data of the file is stored in a typed array. + // Note: typed arrays are not resizable like normal JS arrays are, so + // there is a small penalty involved for appending file writes that + // continuously grow a file similar to std::vector capacity vs used. + node.contents = MEMFS.emptyFileContents ??= new Uint8Array(0); } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node; node.stream_ops = MEMFS.ops_table.link.stream; @@ -1119,36 +1133,29 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return node; }, getFileDataAsTypedArray(node) { - if (!node.contents) return new Uint8Array(0); - if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. - return new Uint8Array(node.contents); + return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. }, expandFileStorage(node, newCapacity) { - var prevCapacity = node.contents ? node.contents.length : 0; + var prevCapacity = node.contents.length; if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. - // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. - // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to - // avoid overshooting the allocation cap by a very large margin. + // Don't expand strictly to the given requested limit if it's only a very + // small increase, but instead geometrically grow capacity. + // For small filesizes (<1MB), perform size*2 geometric increase, but for + // large sizes, do a much more conservative size*1.125 increase to avoid + // overshooting the allocation cap by a very large margin. var CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); - if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. - var oldContents = node.contents; + if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. + var oldContents = MEMFS.getFileDataAsTypedArray(node); node.contents = new Uint8Array(newCapacity); // Allocate new storage. - if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. + node.contents.set(oldContents); }, resizeFileStorage(node, newSize) { if (node.usedBytes == newSize) return; - if (newSize == 0) { - node.contents = null; // Fully decommit when requesting a resize to zero. - node.usedBytes = 0; - } else { - var oldContents = node.contents; - node.contents = new Uint8Array(newSize); // Allocate new storage. - if (oldContents) { - node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. - } - node.usedBytes = newSize; - } + var oldContents = node.contents; + node.contents = new Uint8Array(newSize); // Allocate new storage. + node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. + node.usedBytes = newSize; }, node_ops:{ getattr(node) { @@ -1254,11 +1261,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var contents = stream.node.contents; if (position >= stream.node.usedBytes) return 0; var size = Math.min(stream.node.usedBytes - position, length); - if (size > 8 && contents.subarray) { // non-trivial, and typed array - buffer.set(contents.subarray(position, position + size), offset); - } else { - for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; - } + buffer.set(contents.subarray(position, position + size), offset); return size; }, write(stream, buffer, offset, length, position, canOwn) { @@ -1274,32 +1277,18 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var node = stream.node; node.mtime = node.ctime = Date.now(); - if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? - if (canOwn) { - node.contents = buffer.subarray(offset, offset + length); - node.usedBytes = length; - return length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. - node.contents = buffer.slice(offset, offset + length); - node.usedBytes = length; - return length; - } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? - node.contents.set(buffer.subarray(offset, offset + length), position); - return length; - } - } - - // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. - MEMFS.expandFileStorage(node, position+length); - if (node.contents.subarray && buffer.subarray) { + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + } else { + MEMFS.expandFileStorage(node, position+length); // Use typed array write which is available. node.contents.set(buffer.subarray(offset, offset + length), position); - } else { - for (var i = 0; i < length; i++) { - node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. - } + node.usedBytes = Math.max(node.usedBytes, position + length); } - node.usedBytes = Math.max(node.usedBytes, position + length); return length; }, llseek(stream, offset, whence) { @@ -1324,7 +1313,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var allocated; var contents = stream.node.contents; // Only make a new copy when MAP_PRIVATE is specified. - if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { + if (!(flags & 2) && contents.buffer === HEAP8.buffer) { // We can't emulate MAP_SHARED when the file is not backed by the // buffer we're mapping to (e.g. the HEAP buffer). allocated = false; @@ -1358,6 +1347,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; var FS_modeStringToFlags = (str) => { + if (typeof str != 'string') return str; var flagModes = { 'r': 0, 'r+': 2, @@ -1373,6 +1363,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return flags; }; + var FS_fileDataToTypedArray = (data) => { + if (typeof data == 'string') { + data = intArrayFromString(data, true); + } + if (!data.subarray) { + data = new Uint8Array(data); + } + return data; + }; + var FS_getMode = (canRead, canWrite) => { var mode = 0; if (canRead) mode |= 292 | 73; @@ -1470,8 +1470,6 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { ignorePermissions:true, filesystems:null, syncFSRequests:0, - readFiles:{ - }, ErrnoError:class { name = 'ErrnoError'; // We set the `name` property to be able to identify `FS.ErrnoError` @@ -1737,9 +1735,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // return 0 if any user, group or owner bits are set. if (perms.includes('r') && !(node.mode & 292)) { return 2; - } else if (perms.includes('w') && !(node.mode & 146)) { + } + if (perms.includes('w') && !(node.mode & 146)) { return 2; - } else if (perms.includes('x') && !(node.mode & 73)) { + } + if (perms.includes('x') && !(node.mode & 73)) { return 2; } return 0; @@ -1780,10 +1780,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10; } - } else { - if (FS.isDir(node.mode)) { - return 31; - } + } else if (FS.isDir(node.mode)) { + return 31; } return 0; }, @@ -1793,13 +1791,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } if (FS.isLink(node.mode)) { return 32; - } else if (FS.isDir(node.mode)) { - if (FS.flagsToPermissionString(flags) !== 'r' // opening for write - || (flags & (512 | 64))) { // TODO: check for O_SEARCH? (== search for dir only) + } + var mode = FS.flagsToPermissionString(flags); + if (FS.isDir(node.mode)) { + // opening for write + // TODO: check for O_SEARCH? (== search for dir only) + if (mode !== 'r' || (flags & (512 | 64))) { return 31; } } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + return FS.nodePermissions(node, mode); }, checkOpExists(op, err) { if (!op) { @@ -2367,7 +2368,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (path === "") { throw new FS.ErrnoError(44); } - flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags; + flags = FS_modeStringToFlags(flags); if ((flags & 64)) { mode = (mode & 4095) | 32768; } else { @@ -2454,11 +2455,6 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (created) { FS.chmod(node, mode & 0o777); } - if (Module['logReadFiles'] && !(flags & 1)) { - if (!(path in FS.readFiles)) { - FS.readFiles[path] = 1; - } - } return stream; }, close(stream) { @@ -2605,14 +2601,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { writeFile(path, data, opts = {}) { opts.flags = opts.flags || 577; var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data == 'string') { - data = new Uint8Array(intArrayFromString(data, true)); - } - if (ArrayBuffer.isView(data)) { - FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); - } else { - abort('Unsupported data type'); - } + data = FS_fileDataToTypedArray(data); + FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); FS.close(stream); }, cwd:() => FS.currentPath, @@ -2832,11 +2822,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var mode = FS_getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { - if (typeof data == 'string') { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } + data = FS_fileDataToTypedArray(data); // make sure we can write to the file FS.chmod(node, mode | 146); var stream = FS.open(node, 577); @@ -4160,6 +4146,7 @@ var _sqlite3_status64, _sqlite3_bind_null, _sqlite3_bind_pointer, _sqlite3_bind_text, + _sqlite3_bind_zeroblob, _sqlite3_bind_parameter_count, _sqlite3_bind_parameter_name, _sqlite3_bind_parameter_index, @@ -4423,6 +4410,7 @@ function assignWasmExports(wasmExports) { _sqlite3_bind_null = Module['_sqlite3_bind_null'] = wasmExports['sqlite3_bind_null']; _sqlite3_bind_pointer = Module['_sqlite3_bind_pointer'] = wasmExports['sqlite3_bind_pointer']; _sqlite3_bind_text = Module['_sqlite3_bind_text'] = wasmExports['sqlite3_bind_text']; + _sqlite3_bind_zeroblob = Module['_sqlite3_bind_zeroblob'] = wasmExports['sqlite3_bind_zeroblob']; _sqlite3_bind_parameter_count = Module['_sqlite3_bind_parameter_count'] = wasmExports['sqlite3_bind_parameter_count']; _sqlite3_bind_parameter_name = Module['_sqlite3_bind_parameter_name'] = wasmExports['sqlite3_bind_parameter_name']; _sqlite3_bind_parameter_index = Module['_sqlite3_bind_parameter_index'] = wasmExports['sqlite3_bind_parameter_index']; @@ -4767,6 +4755,7 @@ Module.runSQLite3PostLoadInit = async function( - sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls - sqlite3-vfs-opfs.c-pp.js => OPFS VFS - sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS + - sqlite3-vfs-opfs-wl.c-pp.js => WebLock-using OPFS VFS - post-js-footer.js => this file's epilogue And all of that gets sandwiched between extern-pre-js.js and @@ -4801,11 +4790,11 @@ Module.runSQLite3PostLoadInit = async function( /* @preserve ** This code was built from sqlite3 version... ** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** SQLITE_VERSION "3.53.0" +** SQLITE_VERSION_NUMBER 3053000 +** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** -** Emscripten SDK: 5.0.0 +** Emscripten SDK: 5.0.5 */ /* 2022-05-22 @@ -4908,6 +4897,13 @@ Module.runSQLite3PostLoadInit = async function( used in WASMFS-capable builds of the library (which the canonical builds do not include). + - `disable` (as of 3.53.0) may be an object with the following + properties: + - `vfs`, an object, may contain a map of VFS names to booleans. + Any mapping to falsy are disabled. The supported names + are: "kvvfs", "opfs", "opfs-sahpool", "opfs-wl". + - Other disabling options may be added in the future. + [^1] = This property may optionally be a function, in which case this function calls that function to fetch the value, enabling delayed evaluation. @@ -4954,7 +4950,8 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( ); return sqlite3ApiBootstrap.sqlite3; } - const config = Object.assign(Object.create(null),{ + const nu = (...obj)=>Object.assign(Object.create(null),...obj); + const config = nu({ exports: undefined, memory: undefined, bigIntEnabled: !!globalThis.BigInt64Array, @@ -4971,7 +4968,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( certain wasm.xWrap.resultAdapter()s. */ useStdAlloc: false - }, apiConfig || {}); + }, apiConfig); Object.assign(config, { allocExportName: config.useStdAlloc ? 'malloc' : 'sqlite3_malloc', @@ -5004,7 +5001,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( not documented are installed as 1-to-1 proxies for their C-side counterparts. */ - const capi = Object.create(null); + const capi = nu(); /** Holds state which are specific to the WASM-related infrastructure and glue code. @@ -5013,7 +5010,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( dynamically after the api object is fully constructed, so not all are documented in this file. */ - const wasm = Object.create(null); + const wasm = nu(); /** Internal helper for SQLite3Error ctor. */ const __rcStr = (rc)=>{ @@ -5561,6 +5558,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( toss: function(...args){throw new Error(args.join(' '))}, toss3, typedArrayPart: wasm.typedArrayPart, + nu, assert: function(arg,msg){ if( !arg ){ util.toss("Assertion failed:",msg); @@ -5817,7 +5815,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; }; } - const rc = Object.create(null), ov = [0,0]; + const rc = nu(), ov = [0,0]; let i = 0, k; while((k = capi.sqlite3_compileoption_get(i++))){ f._opt(k,ov); @@ -5825,7 +5823,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( } return f._result = rc; }else if(Array.isArray(optName)){ - const rc = Object.create(null); + const rc = nu(); optName.forEach((v)=>{ rc[v] = capi.sqlite3_compileoption_used(v); }); @@ -5876,7 +5874,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( The memory lives in the WASM heap and can be used with routines such as wasm.poke() and wasm.heap8u().slice(). */ - wasm.pstack = Object.assign(Object.create(null),{ + wasm.pstack = nu({ /** Sets the current pstack position to the given pointer. Results are undefined if the passed-in value did not come from @@ -6098,7 +6096,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( // sqlite3__wasm_init_wasmfs() is not available return this.dir = ""; } - }.bind(Object.create(null)); + }.bind(nu()); /** Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a @@ -6452,6 +6450,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: + case capi.SQLITE_DBCONFIG_FP_DIGITS: if( !this.ip ){ this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int', ['sqlite3*', 'int', 'int', '*']); @@ -6473,7 +6472,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( default: return capi.SQLITE_MISUSE; } - }.bind(Object.create(null)); + }.bind(nu()); /** Given a (sqlite3_value*), this function attempts to convert it @@ -6707,7 +6706,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( if(rc) return SQLite3Error.toss(rc,arguments[2]+"() failed with code "+rc); const pv = wasm.peekPtr(this.ptr); return pv ? capi.sqlite3_value_to_js( pv, true ) : undefined; - }.bind(Object.create(null)); + }.bind(nu()); /** A wrapper around sqlite3_preupdate_new() which fetches the @@ -6747,6 +6746,62 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( 'sqlite3changeset_old'); }/*changeset/preupdate additions*/ + /** + EXPERIMENTAL. For tentative addition in 3.53.0. + + sqlite3_js_retry_busy(maxTimes,callback[,beforeRetry]) + + Calls the given _synchronous_ callback function. If that function + returns sqlite3.capi.SQLITE_BUSY _or_ throws an SQLite3Error + with a resultCode property of that value then it will suppress + that error and try again, up to the given maximum number of + times. If the callback returns any other value than that, + it is returned. If the maximum number of retries has been + reached, an SQLite3Error with a resultCode value of + sqlite3.capi.SQLITE_BUSY is thrown. If the callback throws any + exception other than the aforementioned BUSY exception, it is + propagated. If it throws a BUSY exception on its final attempt, + that is propagated as well. + + If the beforeRetry argument is given, it must be a _synchronous_ + function. It is called immediately before each retry of the + callback (not for the initial call), passed the attempt number + (so it starts with 2, not 1). If it throws, the exception is + handled as described above. Its result value is ignored. + + To effectively retry "forever", pass a negative maxTimes value, + with the caveat that there is no recovery from that unless the + beforeRetry() can figure out when to throw. + + TODO: an async variant of this. + */ + capi.sqlite3_js_retry_busy = function(maxTimes, callback, beforeRetry){ + for(let n = 1; n <= maxTimes; ++n){ + try{ + if( beforeRetry && n>1 ) beforeRetry(n); + const rc = callback(); + if( capi.SQLITE_BUSY===rc ){ + if( n===maxTimes ){ + throw new SQLite3Error(rc, [ + "sqlite3_js_retry_busy() max retry attempts (", + maxTimes, + ") reached." + ].join('')); + } + continue; + } + return rc; + }catch(e){ + if( n{}; const debug = sqlite3.__isUnderTest - ? (...args)=>sqlite3.config.debug("kvvfs:", ...args) + ? (...args)=>sqlite3.config.debug?.("kvvfs:", ...args) : noop; - const warn = (...args)=>sqlite3.config.warn("kvvfs:", ...args); - const error = (...args)=>sqlite3.config.error("kvvfs:", ...args); + const warn = (...args)=>sqlite3.config.warn?.("kvvfs:", ...args); + const error = (...args)=>sqlite3.config.error?.("kvvfs:", ...args); /** Implementation of JS's Storage interface for use as backing store @@ -16237,17 +16299,21 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ and recreating it whenever a property index might be invalidated. */ class KVVfsStorage { - #map; - #keys; - #getKeys(){return this.#keys ??= Object.keys(this.#map);} + #map = Object.create(null); + #keys = null; + #size = 0; constructor(){ this.clear(); } + #getKeys(){ + return this.#keys ??= Object.keys(this.#map); + } + key(n){ - const k = this.#getKeys(); - return n= this.#size) return null; + return this.#getKeys()[n]; } getItem(k){ @@ -16255,14 +16321,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } setItem(k,v){ - if( !hop(this.#map, k) ){ + if( !(k in this.#map) ){ + ++this.#size; this.#keys = null; } this.#map[k] = ''+v; } removeItem(k){ - if( delete this.#map[k] ){ + if( k in this.#map ){ + delete this.#map[k]; + --this.#size; this.#keys = null; } } @@ -16270,10 +16339,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ clear(){ this.#map = Object.create(null); this.#keys = null; + this.#size = 0; } get length() { - return this.#getKeys().length; + return this.#size; } }/*KVVfsStorage*/; @@ -17035,36 +17105,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }, - // We override xRead/xWrite only for logging/debugging. They - // should otherwise be disabled (it's faster that way). - xRead: function(pFile,pTgt,n,iOff64){ - cache.popError(); - try{ - if( kvvfs?.log?.xRead ){ - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xRead", n, iOff64, h); - } - return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); - }catch(e){ - error("xRead",e); - return cache.setError(e); - } - }, - xWrite: function(pFile,pSrc,n,iOff64){ - cache.popError(); - try{ - if( kvvfs?.log?.xWrite ){ - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xWrite", n, iOff64, h); - } - return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); - }catch(e){ - error("xWrite",e); - return cache.setError(e); - } - }, }/*.ioDb*/, @@ -17076,9 +17116,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }/*.ioJrnl*/ }/*methodOverrides*/; - debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, - kvvfsMethods, capi.sqlite3_file.structInfo, - KVVfsFile.structInfo); + try { util.assert( cache.buffer.n>1024*129, "Heap buffer is not large enough" /* Native is SQLITE_KVOS_SZ is 133073 as of this writing */ ); @@ -17167,7 +17205,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ limitation which has since been overcome, but removal of JsStorageDb.prototype.clearStorage() would be a backwards compatibility break, so this function permits wiping the storage for those two - cases even if they are opened. Use with case. + cases even if they are opened. Use with care. */ const sqlite3_js_kvvfs_clear = function callee(which){ if( ''===which ){ @@ -17842,7 +17880,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } return rc; }catch(e){ - return VT.xErrror('xConnect', e, capi.SQLITE_ERROR); + return VT.xError('xConnect', e, capi.SQLITE_ERROR); } }, xCreate: wasm.ptr.null, // eponymous only @@ -17940,7 +17978,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ })/*globalThis.sqlite3ApiBootstrap.initializers*/; -/* The OPFS VFS parts are elided from builds targeting node.js. */ /* The OPFS SAH Pool VFS parts are elided from builds targeting node.js. diff --git a/src/bin/sqlite3-opfs-async-proxy.js b/src/bin/sqlite3-opfs-async-proxy.js index 79fc473..e09bbbd 100644 --- a/src/bin/sqlite3-opfs-async-proxy.js +++ b/src/bin/sqlite3-opfs-async-proxy.js @@ -46,10 +46,23 @@ theFunc().then(...) is not compatible with the change to synchronous, but we do do not use those APIs that way. i.e. we don't _need_ to change anything for this, but at some point (after Chrome - versions (approximately) 104-107 are extinct) should change our + versions (approximately) 104-107 are extinct) we should change our usage of those methods to remove the "await". */ + "use strict"; +const urlParams = new URL(globalThis.location.href).searchParams; +const vfsName = urlParams.get('vfs'); +if( !vfsName ){ + throw new Error("Expecting vfs=opfs|opfs-wl URL argument for this worker"); +} +/** + We use this to allow us to differentiate debug output from + multiple instances, e.g. multiple Workers to the "opfs" + VFS or both the "opfs" and "opfs-wl" VFSes. +*/ +const workerId = (Math.random() * 10000000) | 0; +const isWebLocker = 'opfs-wl'===urlParams.get('vfs'); const wPost = (type,...args)=>postMessage({type, payload:args}); const installAsyncProxy = function(){ const toss = function(...args){throw new Error(args.join(' '))}; @@ -66,6 +79,174 @@ const installAsyncProxy = function(){ */ const state = Object.create(null); + /* initS11n() is preprocessor-injected so that we have identical + copies in the synchronous and async halves. This side does not + load the SQLite library, so does not have access to that copy. */ +const initS11n = function(){ + /** + This proxy de/serializes cross-thread function arguments and + output-pointer values via the state.sabIO SharedArrayBuffer, + using the region defined by (state.sabS11nOffset, + state.sabS11nOffset + state.sabS11nSize]. Only one dataset is + recorded at a time. + + This is not a general-purpose format. It only supports the + range of operations, and data sizes, needed by the + sqlite3_vfs and sqlite3_io_methods operations. Serialized + data are transient and this serialization algorithm may + change at any time. + + The data format can be succinctly summarized as: + + Nt...Td...D + + Where: + + - N = number of entries (1 byte) + + - t = type ID of first argument (1 byte) + + - ...T = type IDs of the 2nd and subsequent arguments (1 byte + each). + + - d = raw bytes of first argument (per-type size). + + - ...D = raw bytes of the 2nd and subsequent arguments (per-type + size). + + All types except strings have fixed sizes. Strings are stored + using their TextEncoder/TextDecoder representations. It would + arguably make more sense to store them as Int16Arrays of + their JS character values, but how best/fastest to get that + in and out of string form is an open point. Initial + experimentation with that approach did not gain us any speed. + + Historical note: this impl was initially about 1% this size by + using using JSON.stringify/parse(), but using fit-to-purpose + serialization saves considerable runtime. + */ + if(state.s11n) return state.s11n; + const textDecoder = new TextDecoder(), + textEncoder = new TextEncoder('utf-8'), + viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), + viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + state.s11n = Object.create(null); + /* Only arguments and return values of these types may be + serialized. This covers the whole range of types needed by the + sqlite3_vfs API. */ + const TypeIds = Object.create(null); + TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; + TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; + TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; + TypeIds.string = { id: 4 }; + + const getTypeId = (v)=>( + TypeIds[typeof v] + || toss("Maintenance required: this value type cannot be serialized.",v) + ); + const getTypeIdById = (tid)=>{ + switch(tid){ + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:",tid); + } + }; + + /** + Returns an array of the deserialized state stored by the most + recent serialize() operation (from this thread or the + counterpart thread), or null if the serialization buffer is + empty. If passed a truthy argument, the serialization buffer + is cleared after deserialization. + */ + state.s11n.deserialize = function(clear=false){ + const t = performance.now(); + const argc = viewU8[0]; + const rc = argc ? [] : null; + if(argc){ + const typeIds = []; + let offset = 1, i, n, v; + for(i = 0; i < argc; ++i, ++offset){ + typeIds.push(getTypeIdById(viewU8[offset])); + } + for(i = 0; i < argc; ++i){ + const t = typeIds[i]; + if(t.getter){ + v = viewDV[t.getter](offset, state.littleEndian); + offset += t.size; + }else{/*String*/ + n = viewDV.getInt32(offset, state.littleEndian); + offset += 4; + v = textDecoder.decode(viewU8.slice(offset, offset+n)); + offset += n; + } + rc.push(v); + } + } + if(clear) viewU8[0] = 0; + //log("deserialize:",argc, rc); + return rc; + }; + + /** + Serializes all arguments to the shared buffer for consumption + by the counterpart thread. + + This routine is only intended for serializing OPFS VFS + arguments and (in at least one special case) result values, + and the buffer is sized to be able to comfortably handle + those. + + If passed no arguments then it zeroes out the serialization + state. + */ + state.s11n.serialize = function(...args){ + const t = performance.now(); + if(args.length){ + //log("serialize():",args); + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 0xff /* header = # of args */; + for(; i < args.length; ++i, ++offset){ + /* Write the TypeIds.id value into the next args.length + bytes. */ + typeIds.push(getTypeId(args[i])); + viewU8[offset] = typeIds[i].id; + } + for(i = 0; i < args.length; ++i) { + /* Deserialize the following bytes based on their + corresponding TypeIds.id from the header. */ + const t = typeIds[i]; + if(t.setter){ + viewDV[t.setter](offset, args[i], state.littleEndian); + offset += t.size; + }else{/*String*/ + const s = textEncoder.encode(args[i]); + viewDV.setInt32(offset, s.byteLength, state.littleEndian); + offset += 4; + viewU8.set(s, offset); + offset += s.byteLength; + } + } + //log("serialize() result:",viewU8.slice(0,offset)); + }else{ + viewU8[0] = 0; + } + }; + + state.s11n.storeException = state.asyncS11nExceptions + ? ((priority,e)=>{ + if(priority<=state.asyncS11nExceptions){ + state.s11n.serialize([e.name,': ',e.message].join("")); + } + }) + : ()=>{}; + + return state.s11n; +}/*initS11n()*/; + /** verbose: @@ -82,7 +263,7 @@ const installAsyncProxy = function(){ 2:console.log.bind(console) }; const logImpl = (level,...args)=>{ - if(state.verbose>level) loggers[level]("OPFS asyncer:",...args); + if(state.verbose>level) loggers[level](vfsName+' async-proxy',workerId+":",...args); }; const log = (...args)=>logImpl(2, ...args); const warn = (...args)=>logImpl(1, ...args); @@ -97,12 +278,13 @@ const installAsyncProxy = function(){ */ const __openFiles = Object.create(null); /** - __implicitLocks is a Set of sqlite3_file pointers (integers) which were - "auto-locked". i.e. those for which we obtained a sync access - handle without an explicit xLock() call. Such locks will be - released during db connection idle time, whereas a sync access - handle obtained via xLock(), or subsequently xLock()'d after - auto-acquisition, will not be released until xUnlock() is called. + __implicitLocks is a Set of sqlite3_file pointers (integers) + which were "auto-locked". i.e. those for which we necessarily + obtain a sync access handle without an explicit xLock() call + guarding access. Such locks will be released during + `waitLoop()`'s idle time, whereas a sync access handle obtained + via xLock(), or subsequently xLock()'d after auto-acquisition, + will not be released until xUnlock() is called. Maintenance reminder: if we relinquish auto-locks at the end of the operation which acquires them, we pay a massive performance @@ -271,10 +453,11 @@ const installAsyncProxy = function(){ In order to help alleviate cross-tab contention for a dabase, if an exception is thrown while acquiring the handle, this routine - will wait briefly and try again, up to some fixed number of - times. If acquisition still fails at that point it will give up - and propagate the exception. Client-level code will see that as - an I/O error. + will wait briefly and try again, up to `maxTries` of times. If + acquisition still fails at that point it will give up and + propagate the exception. Client-level code will see that either + as an I/O error or SQLITE_BUSY, depending on the exception and + the context. 2024-06-12: there is a rare race condition here which has been reported a single time: @@ -289,13 +472,31 @@ const installAsyncProxy = function(){ there's another race condition there). That's easy to say but creating a viable test for that condition has proven challenging so far. + + Interface quirk: if fh.xLock is falsy and the handle is acquired + then fh.fid is added to __implicitLocks(). If fh.xLock is truthy, + it is not added as an implicit lock. i.e. xLock() impls must set + fh.xLock immediately _before_ calling this and must arrange to + restore it to its previous value if this function throws. + + 2026-03-06: + + - baseWaitTime is the number of milliseconds to wait for the + first retry, increasing by one factor for each retry. It defaults + to (state.asyncIdleWaitTime*2). + + - maxTries is the number of attempt to make, each one spaced out + by one additional factor of the baseWaitTime (e.g. 300, then 600, + then 900, the 1200...). This MUST be an integer >0. + + Only the Web Locks impl should use the 3rd and 4th parameters. */ - const getSyncHandle = async (fh,opName)=>{ + const getSyncHandle = async (fh, opName, baseWaitTime, maxTries = 6)=>{ if(!fh.syncHandle){ const t = performance.now(); log("Acquiring sync handle for",fh.filenameAbs); - const maxTries = 6, - msBase = state.asyncIdleWaitTime * 2; + const msBase = baseWaitTime ?? (state.asyncIdleWaitTime * 2); + maxTries ??= 6; let i = 1, ms = msBase; for(; true; ms = msBase * ++i){ try { @@ -329,6 +530,9 @@ const installAsyncProxy = function(){ /** Stores the given value at state.sabOPView[state.opIds.rc] and then Atomics.notify()'s it. + + The opName is only used for logging and debugging - all result + codes are expected on the same state.sabOPView slot. */ const storeAndNotify = (opName, value)=>{ log(opName+"() => notify(",value,")"); @@ -458,24 +662,12 @@ const installAsyncProxy = function(){ await releaseImplicitLock(fh); storeAndNotify('xFileSize', rc); }, - xLock: async function(fid/*sqlite3_file pointer*/, - lockType/*SQLITE_LOCK_...*/){ - const fh = __openFiles[fid]; - let rc = 0; - const oldLockType = fh.xLock; - fh.xLock = lockType; - if( !fh.syncHandle ){ - try { - await getSyncHandle(fh,'xLock'); - __implicitLocks.delete(fid); - }catch(e){ - state.s11n.storeException(1,e); - rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK); - fh.xLock = oldLockType; - } - } - storeAndNotify('xLock',rc); - }, + /** + The first argument is semantically invalid here - it's an + address in the synchronous side's heap. We can do nothing with + it here except use it as a unique-per-file identifier. + i.e. a lookup key. + */ xOpen: async function(fid/*sqlite3_file pointer*/, filename, flags/*SQLITE_OPEN_...*/, opfsFlags/*OPFS_...*/){ @@ -533,7 +725,7 @@ const installAsyncProxy = function(){ rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; } }catch(e){ - error("xRead() failed",e,fh); + //error("xRead() failed",e,fh); state.s11n.storeException(1,e); rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_READ); } @@ -560,29 +752,13 @@ const installAsyncProxy = function(){ affirmNotRO('xTruncate', fh); await (await getSyncHandle(fh,'xTruncate')).truncate(size); }catch(e){ - error("xTruncate():",e,fh); + //error("xTruncate():",e,fh); state.s11n.storeException(2,e); rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_TRUNCATE); } await releaseImplicitLock(fh); storeAndNotify('xTruncate',rc); }, - xUnlock: async function(fid/*sqlite3_file pointer*/, - lockType/*SQLITE_LOCK_...*/){ - let rc = 0; - const fh = __openFiles[fid]; - if( fh.syncHandle - && state.sq3Codes.SQLITE_LOCK_NONE===lockType - /* Note that we do not differentiate between lock types in - this VFS. We're either locked or unlocked. */ ){ - try { await closeSyncHandle(fh) } - catch(e){ - state.s11n.storeException(1,e); - rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; - } - } - storeAndNotify('xUnlock',rc); - }, xWrite: async function(fid/*sqlite3_file pointer*/,n,offset64){ let rc; const fh = __openFiles[fid]; @@ -594,7 +770,7 @@ const installAsyncProxy = function(){ {at: Number(offset64)}) ) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; }catch(e){ - error("xWrite():",e,fh); + //error("xWrite():",e,fh); state.s11n.storeException(1,e); rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_WRITE); } @@ -603,152 +779,269 @@ const installAsyncProxy = function(){ } }/*vfsAsyncImpls*/; - const initS11n = ()=>{ - /** - ACHTUNG: this code is 100% duplicated in the other half of this - proxy! The documentation is maintained in the "synchronous half". + if( isWebLocker ){ + /* We require separate xLock() and xUnlock() implementations for the + original and Web Lock implementations. The ones in this block + are for the WebLock impl. + + The Golden Rule for this impl is: if we have a web lock, we + must also hold the SAH. When "upgrading" an implicit lock to a + requested (explicit) lock, we must remove the SAH from the + __implicitLocks set. When we unlock, we release both the web + lock and the SAH. That invariant must be kept intact or race + conditions on SAHs will ensue. */ - if(state.s11n) return state.s11n; - const textDecoder = new TextDecoder(), - textEncoder = new TextEncoder('utf-8'), - viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), - viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - state.s11n = Object.create(null); - const TypeIds = Object.create(null); - TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; - TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; - TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; - TypeIds.string = { id: 4 }; - const getTypeId = (v)=>( - TypeIds[typeof v] - || toss("Maintenance required: this value type cannot be serialized.",v) - ); - const getTypeIdById = (tid)=>{ - switch(tid){ - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:",tid); - } - }; - state.s11n.deserialize = function(clear=false){ - const argc = viewU8[0]; - const rc = argc ? [] : null; - if(argc){ - const typeIds = []; - let offset = 1, i, n, v; - for(i = 0; i < argc; ++i, ++offset){ - typeIds.push(getTypeIdById(viewU8[offset])); + /** Registry of active Web Locks: fid -> { mode, resolveRelease } */ + const __activeWebLocks = Object.create(null); + + vfsAsyncImpls.xLock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/, + isFromUnlock/*only if called from this.xUnlock()*/){ + const whichOp = isFromUnlock ? 'xUnlock' : 'xLock'; + const fh = __openFiles[fid]; + //error("xLock()",fid, lockType, isFromUnlock, fh); + const requestedMode = (lockType >= state.sq3Codes.SQLITE_LOCK_RESERVED) + ? 'exclusive' : 'shared'; + const existing = __activeWebLocks[fid]; + if( existing ){ + if( existing.mode === requestedMode + || (existing.mode === 'exclusive' + && requestedMode === 'shared') ) { + fh.xLock = lockType; + storeAndNotify(whichOp, 0); + /* Don't do this: existing.mode = requestedMode; + + Paraphrased from advice given by a consulting developer: + + If you hold an exclusive lock and SQLite requests shared, + you should keep exiting.mode as exclusive in because the + underlying Web Lock is still exclusive. Changing it to + shared would trick xLock into thinking it needs to + perform a release/re-acquire dance if an exclusive is + later requested. + */ + return 0 /* Already held at required or higher level */; } - for(i = 0; i < argc; ++i){ - const t = typeIds[i]; - if(t.getter){ - v = viewDV[t.getter](offset, state.littleEndian); - offset += t.size; - }else{/*String*/ - n = viewDV.getInt32(offset, state.littleEndian); - offset += 4; - v = textDecoder.decode(viewU8.slice(offset, offset+n)); - offset += n; + /* + Upgrade path: we must release shared and acquire exclusive. + This transition is NOT atomic in Web Locks API. + + It _effectively_ is atomic if we don't call + closeSyncHandle(fh), as no other worker can lock that until + we let it go. But we can't do that without eventually + leading to deadly embrace situations, so we don't do that. + (That's not a hypothetical, it has happened.) + */ + await closeSyncHandle(fh); + existing.resolveRelease(); + delete __activeWebLocks[fid]; + } + + const lockName = "sqlite3-vfs-opfs:" + fh.filenameAbs; + const oldLockType = fh.xLock; + return new Promise((resolveWaitLoop) => { + //log("xLock() initial promise entered..."); + navigator.locks.request(lockName, { mode: requestedMode }, async (lock) => { + //log("xLock() Web Lock entered.", fh); + __implicitLocks.delete(fid); + let rc = 0; + try{ + fh.xLock = lockType/*must be set before getSyncHandle() is called!*/; + await getSyncHandle(fh, 'xLock', state.asyncIdleWaitTime, 5); + }catch(e){ + fh.xLock = oldLockType; + state.s11n.storeException(1, e); + rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_BUSY); } - rc.push(v); - } + const releasePromise = rc + ? undefined + : new Promise((resolveRelease) => { + __activeWebLocks[fid] = { mode: requestedMode, resolveRelease }; + }); + storeAndNotify(whichOp, rc) /* unblock the C side */; + resolveWaitLoop(0) /* unblock waitLoop() */; + await releasePromise /* hold the lock until xUnlock */; + }); + }); + }; + + /** Internal helper for the opfs-wl xUnlock() */ + const wlCloseHandle = async(fh)=>{ + let rc = 0; + try{ + /* For the record, we've never once seen closeSyncHandle() + throw, nor should it because destructors do not throw. */ + await closeSyncHandle(fh); + }catch(e){ + state.s11n.storeException(1,e); + rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; } - if(clear) viewU8[0] = 0; - //log("deserialize:",argc, rc); return rc; }; - state.s11n.serialize = function(...args){ - if(args.length){ - //log("serialize():",args); - const typeIds = []; - let i = 0, offset = 1; - viewU8[0] = args.length & 0xff /* header = # of args */; - for(; i < args.length; ++i, ++offset){ - /* Write the TypeIds.id value into the next args.length - bytes. */ - typeIds.push(getTypeId(args[i])); - viewU8[offset] = typeIds[i].id; - } - for(i = 0; i < args.length; ++i) { - /* Deserialize the following bytes based on their - corresponding TypeIds.id from the header. */ - const t = typeIds[i]; - if(t.setter){ - viewDV[t.setter](offset, args[i], state.littleEndian); - offset += t.size; - }else{/*String*/ - const s = textEncoder.encode(args[i]); - viewDV.setInt32(offset, s.byteLength, state.littleEndian); - offset += 4; - viewU8.set(s, offset); - offset += s.byteLength; - } + + vfsAsyncImpls.xUnlock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/){ + const fh = __openFiles[fid]; + const existing = __activeWebLocks[fid]; + if( !existing ){ + const rc = await wlCloseHandle(fh); + storeAndNotify('xUnlock', rc); + return rc; + } + //log("xUnlock()",fid, lockType, fh); + let rc = 0; + if( lockType === state.sq3Codes.SQLITE_LOCK_NONE ){ + /* SQLite usually unlocks all the way to NONE */ + rc = await wlCloseHandle(fh); + existing.resolveRelease(); + delete __activeWebLocks[fid]; + fh.xLock = lockType; + }else if( lockType === state.sq3Codes.SQLITE_LOCK_SHARED + && existing.mode === 'exclusive' ){ + /* downgrade EXCLUSIVE -> SHARED */ + rc = await wlCloseHandle(fh); + if( 0===rc ){ + fh.xLock = lockType; + existing.resolveRelease(); + delete __activeWebLocks[fid]; + return vfsAsyncImpls.xLock(fid, lockType, true); } - //log("serialize() result:",viewU8.slice(0,offset)); }else{ - viewU8[0] = 0; + /* ??? */ + error("xUnlock() unhandled condition", fh); + } + storeAndNotify('xUnlock', rc); + return 0; + } + + }else{ + /* Original/"legacy" xLock() and xUnlock() */ + + vfsAsyncImpls.xLock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/){ + const fh = __openFiles[fid]; + let rc = 0; + const oldLockType = fh.xLock; + fh.xLock = lockType; + if( !fh.syncHandle ){ + try { + await getSyncHandle(fh,'xLock'); + __implicitLocks.delete(fid); + }catch(e){ + state.s11n.storeException(1,e); + rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK); + fh.xLock = oldLockType; + } } + storeAndNotify('xLock',rc); }; - state.s11n.storeException = state.asyncS11nExceptions - ? ((priority,e)=>{ - if(priority<=state.asyncS11nExceptions){ - state.s11n.serialize([e.name,': ',e.message].join("")); + vfsAsyncImpls.xUnlock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/){ + let rc = 0; + const fh = __openFiles[fid]; + if( fh.syncHandle + && state.sq3Codes.SQLITE_LOCK_NONE===lockType + /* Note that we do not differentiate between lock types in + this VFS. We're either locked or unlocked. */ ){ + try { await closeSyncHandle(fh) } + catch(e){ + state.s11n.storeException(1,e); + rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; } - }) - : ()=>{}; + } + storeAndNotify('xUnlock',rc); + } - return state.s11n; - }/*initS11n()*/; + }/*xLock() and xUnlock() impls*/ const waitLoop = async function f(){ - const opHandlers = Object.create(null); - for(let k of Object.keys(state.opIds)){ - const vi = vfsAsyncImpls[k]; - if(!vi) continue; - const o = Object.create(null); - opHandlers[state.opIds[k]] = o; - o.key = k; - o.f = vi; + if( !f.inited ){ + f.inited = true; + f.opHandlers = Object.create(null); + for(let k of Object.keys(state.opIds)){ + const vi = vfsAsyncImpls[k]; + if(!vi) continue; + const o = Object.create(null); + f.opHandlers[state.opIds[k]] = o; + o.key = k; + o.f = vi; + } } + const opIds = state.opIds; + const opView = state.sabOPView; + const slotWhichOp = opIds.whichOp; + const idleWaitTime = state.asyncIdleWaitTime; + const hasWaitAsync = !!Atomics.waitAsync; while(!flagAsyncShutdown){ try { - if('not-equal'!==Atomics.wait( - state.sabOPView, state.opIds.whichOp, 0, state.asyncIdleWaitTime - )){ - /* Maintenance note: we compare against 'not-equal' because - - https://github.com/tomayac/sqlite-wasm/issues/12 - - is reporting that this occasionally, under high loads, - returns 'ok', which leads to the whichOp being 0 (which - isn't a valid operation ID and leads to an exception, - along with a corresponding ugly console log - message). Unfortunately, the conditions for that cannot - be reliably reproduced. The only place in our code which - writes a 0 to the state.opIds.whichOp SharedArrayBuffer - index is a few lines down from here, and that instance - is required in order for clear communication between - the sync half of this proxy and this half. + let opId; + if( hasWaitAsync ){ + opId = Atomics.load(opView, slotWhichOp); + if( 0===opId ){ + const rv = Atomics.waitAsync(opView, slotWhichOp, 0, + idleWaitTime); + if( rv.async ) await rv.value; + await releaseImplicitLocks(); + continue; + } + }else{ + /** + For browsers without Atomics.waitAsync(), we require + the legacy implementation. Browser versions where + waitAsync() arrived: + + Chrome: 90 (2021-04-13) + Firefox: 145 (2025-11-11) + Safari: 16.4 (2023-03-27) + + The "opfs" VFS was not born until Chrome was somewhere in + the v104-108 range (Summer/Autumn 2022) and did not work + with Safari < v17 (2023-09-18) due to a WebKit bug which + restricted OPFS access from sub-Workers. + + The waitAsync() counterpart of this block can be used by + both "opfs" and "opfs-wl", whereas this block can only be + used by "opfs". Performance comparisons between the two + in high-contention tests have been indecisive. */ - await releaseImplicitLocks(); - continue; + if('not-equal'!==Atomics.wait( + state.sabOPView, slotWhichOp, 0, state.asyncIdleWaitTime + )){ + /* Maintenance note: we compare against 'not-equal' because + + https://github.com/tomayac/sqlite-wasm/issues/12 + + is reporting that this occasionally, under high loads, + returns 'ok', which leads to the whichOp being 0 (which + isn't a valid operation ID and leads to an exception, + along with a corresponding ugly console log + message). Unfortunately, the conditions for that cannot + be reliably reproduced. The only place in our code which + writes a 0 to the state.opIds.whichOp SharedArrayBuffer + index is a few lines down from here, and that instance + is required in order for clear communication between + the sync half of this proxy and this half. + + Much later (2026-03-07): that phenomenon is apparently + called a spurious wakeup. + */ + await releaseImplicitLocks(); + continue; + } + opId = Atomics.load(state.sabOPView, slotWhichOp); } - const opId = Atomics.load(state.sabOPView, state.opIds.whichOp); - Atomics.store(state.sabOPView, state.opIds.whichOp, 0); - const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId); + Atomics.store(opView, slotWhichOp, 0); + const hnd = f.opHandlers[opId]?.f ?? toss("No waitLoop handler for whichOp #",opId); const args = state.s11n.deserialize( true /* clear s11n to keep the caller from confusing this with an exception string written by the upcoming operation */ ) || []; - //warn("waitLoop() whichOp =",opId, hnd, args); - if(hnd.f) await hnd.f(...args); - else error("Missing callback for opId",opId); + //error("waitLoop() whichOp =",opId, f.opHandlers[opId].key, args); + await hnd(...args); }catch(e){ - error('in waitLoop():',e); + error('in waitLoop():', e); } } }; @@ -756,6 +1049,7 @@ const installAsyncProxy = function(){ navigator.storage.getDirectory().then(function(d){ state.rootDir = d; globalThis.onmessage = function({data}){ + //log(globalThis.location.href,"onmessage()",data); switch(data.type){ case 'opfs-async-init':{ /* Receive shared state from synchronous partner */ @@ -771,6 +1065,7 @@ const installAsyncProxy = function(){ } }); initS11n(); + //warn("verbosity =",opt.verbose, state.verbose); log("init state",state); wPost('opfs-async-inited'); waitLoop(); @@ -782,22 +1077,27 @@ const installAsyncProxy = function(){ flagAsyncShutdown = false; waitLoop(); } - break; + break; } }; wPost('opfs-async-loaded'); }).catch((e)=>error("error initializing OPFS asyncer:",e)); }/*installAsyncProxy()*/; -if(!globalThis.SharedArrayBuffer){ +if(globalThis.window === globalThis){ + wPost('opfs-unavailable', + "This code cannot run from the main thread.", + "Load it as a Worker from a separate Worker."); +}else if(!globalThis.SharedArrayBuffer){ wPost('opfs-unavailable', "Missing SharedArrayBuffer API.", "The server must emit the COOP/COEP response headers to enable that."); }else if(!globalThis.Atomics){ wPost('opfs-unavailable', "Missing Atomics API.", "The server must emit the COOP/COEP response headers to enable that."); +}else if(isWebLocker && !globalThis.Atomics.waitAsync){ + wPost('opfs-unavailable',"Missing required Atomics.waitSync() for "+vfsName); }else if(!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || - !globalThis.FileSystemFileHandle || - !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || + !globalThis.FileSystemFileHandle?.prototype?.createSyncAccessHandle || !navigator?.storage?.getDirectory){ wPost('opfs-unavailable',"Missing required OPFS APIs."); }else{ diff --git a/src/bin/sqlite3.mjs b/src/bin/sqlite3.mjs index 900dc64..1b7c6ab 100644 --- a/src/bin/sqlite3.mjs +++ b/src/bin/sqlite3.mjs @@ -27,11 +27,11 @@ /* @preserve ** This code was built from sqlite3 version... ** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** SQLITE_VERSION "3.53.0" +** SQLITE_VERSION_NUMBER 3053000 +** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** -** Emscripten SDK: 5.0.0 +** Emscripten SDK: 5.0.5 */ // This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. @@ -92,7 +92,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR /** This file was preprocessed using: - ./c-pp-lite -o ./bld/pre-js.esm.js -Dtarget:es6-module -DModule.instantiateWasm api/pre-js.c-pp.js + ./c-pp -o ./bld/pre-js.esm.js -Dtarget:es6-module -DModule.instantiateWasm api/pre-js.c-pp.js */ (function(Module){ const sIMS = @@ -302,37 +302,17 @@ var isFileURI = (filename) => filename.startsWith('file://'); // include: runtime_stack_check.js // end include: runtime_stack_check.js // include: runtime_exceptions.js +// Base Emscripten EH error class +class EmscriptenEH {} + +class EmscriptenSjLj extends EmscriptenEH {} + // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js var readyPromiseResolve, readyPromiseReject; // Memory management -var -/** @type {!Int8Array} */ - HEAP8, -/** @type {!Uint8Array} */ - HEAPU8, -/** @type {!Int16Array} */ - HEAP16, -/** @type {!Uint16Array} */ - HEAPU16, -/** @type {!Int32Array} */ - HEAP32, -/** @type {!Uint32Array} */ - HEAPU32, -/** @type {!Float32Array} */ - HEAPF32, -/** @type {!Float64Array} */ - HEAPF64; - -// BigInt64Array type is not correctly defined in closure -var -/** not-@type {!BigInt64Array} */ - HEAP64, -/* BigUint64Array type is not correctly defined in closure -/** not-@type {!BigUint64Array} */ - HEAPU64; var runtimeInitialized = false; @@ -430,11 +410,14 @@ function postRun() { // End ATPOSTRUNS hooks } -/** @param {string|number=} what */ +/** + * @param {string|number=} what + * @noreturn + */ function abort(what) { Module['onAbort']?.(what); - what = 'Aborted(' + what + ')'; + what = `Aborted(${what})`; // TODO(sbc): Should we remove printing and leave it up to whoever // catches the exception? err(what); @@ -605,6 +588,36 @@ async function createWasm() { } } + /** @type {!Int16Array} */ + var HEAP16; + + /** @type {!Int32Array} */ + var HEAP32; + + /** not-@type {!BigInt64Array} */ + var HEAP64; + + /** @type {!Int8Array} */ + var HEAP8; + + /** @type {!Float32Array} */ + var HEAPF32; + + /** @type {!Float64Array} */ + var HEAPF64; + + /** @type {!Uint16Array} */ + var HEAPU16; + + /** @type {!Uint32Array} */ + var HEAPU32; + + /** not-@type {!BigUint64Array} */ + var HEAPU64; + + /** @type {!Uint8Array} */ + var HEAPU8; + var callRuntimeCallbacks = (callbacks) => { while (callbacks.length > 0) { // Pass the module as the first argument. @@ -730,12 +743,9 @@ join2:(l, r) => PATH.normalize(l + '/' + r), var initRandomFill = () => { - return (view) => crypto.getRandomValues(view); - }; -var randomFill = (view) => { - // Lazily init on the first invocation. - (randomFill = initRandomFill())(view); + return (view) => (crypto.getRandomValues(view), 0); }; +var randomFill = (view) => (randomFill = initRandomFill())(view); @@ -1131,11 +1141,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node; node.stream_ops = MEMFS.ops_table.file.stream; - node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. - // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred - // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size - // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. - node.contents = null; + // The actual number of bytes used in the typed array, as opposed to + // contents.length which gives the whole capacity. + node.usedBytes = 0; + // The byte data of the file is stored in a typed array. + // Note: typed arrays are not resizable like normal JS arrays are, so + // there is a small penalty involved for appending file writes that + // continuously grow a file similar to std::vector capacity vs used. + node.contents = MEMFS.emptyFileContents ??= new Uint8Array(0); } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node; node.stream_ops = MEMFS.ops_table.link.stream; @@ -1152,36 +1165,29 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return node; }, getFileDataAsTypedArray(node) { - if (!node.contents) return new Uint8Array(0); - if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. - return new Uint8Array(node.contents); + return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. }, expandFileStorage(node, newCapacity) { - var prevCapacity = node.contents ? node.contents.length : 0; + var prevCapacity = node.contents.length; if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. - // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. - // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to - // avoid overshooting the allocation cap by a very large margin. + // Don't expand strictly to the given requested limit if it's only a very + // small increase, but instead geometrically grow capacity. + // For small filesizes (<1MB), perform size*2 geometric increase, but for + // large sizes, do a much more conservative size*1.125 increase to avoid + // overshooting the allocation cap by a very large margin. var CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); - if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. - var oldContents = node.contents; + if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. + var oldContents = MEMFS.getFileDataAsTypedArray(node); node.contents = new Uint8Array(newCapacity); // Allocate new storage. - if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. + node.contents.set(oldContents); }, resizeFileStorage(node, newSize) { if (node.usedBytes == newSize) return; - if (newSize == 0) { - node.contents = null; // Fully decommit when requesting a resize to zero. - node.usedBytes = 0; - } else { - var oldContents = node.contents; - node.contents = new Uint8Array(newSize); // Allocate new storage. - if (oldContents) { - node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. - } - node.usedBytes = newSize; - } + var oldContents = node.contents; + node.contents = new Uint8Array(newSize); // Allocate new storage. + node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. + node.usedBytes = newSize; }, node_ops:{ getattr(node) { @@ -1287,11 +1293,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var contents = stream.node.contents; if (position >= stream.node.usedBytes) return 0; var size = Math.min(stream.node.usedBytes - position, length); - if (size > 8 && contents.subarray) { // non-trivial, and typed array - buffer.set(contents.subarray(position, position + size), offset); - } else { - for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; - } + buffer.set(contents.subarray(position, position + size), offset); return size; }, write(stream, buffer, offset, length, position, canOwn) { @@ -1307,32 +1309,18 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var node = stream.node; node.mtime = node.ctime = Date.now(); - if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? - if (canOwn) { - node.contents = buffer.subarray(offset, offset + length); - node.usedBytes = length; - return length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. - node.contents = buffer.slice(offset, offset + length); - node.usedBytes = length; - return length; - } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? - node.contents.set(buffer.subarray(offset, offset + length), position); - return length; - } - } - - // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. - MEMFS.expandFileStorage(node, position+length); - if (node.contents.subarray && buffer.subarray) { + if (canOwn) { + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + node.contents = buffer.slice(offset, offset + length); + node.usedBytes = length; + } else { + MEMFS.expandFileStorage(node, position+length); // Use typed array write which is available. node.contents.set(buffer.subarray(offset, offset + length), position); - } else { - for (var i = 0; i < length; i++) { - node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. - } + node.usedBytes = Math.max(node.usedBytes, position + length); } - node.usedBytes = Math.max(node.usedBytes, position + length); return length; }, llseek(stream, offset, whence) { @@ -1357,7 +1345,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var allocated; var contents = stream.node.contents; // Only make a new copy when MAP_PRIVATE is specified. - if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) { + if (!(flags & 2) && contents.buffer === HEAP8.buffer) { // We can't emulate MAP_SHARED when the file is not backed by the // buffer we're mapping to (e.g. the HEAP buffer). allocated = false; @@ -1391,6 +1379,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; var FS_modeStringToFlags = (str) => { + if (typeof str != 'string') return str; var flagModes = { 'r': 0, 'r+': 2, @@ -1406,6 +1395,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return flags; }; + var FS_fileDataToTypedArray = (data) => { + if (typeof data == 'string') { + data = intArrayFromString(data, true); + } + if (!data.subarray) { + data = new Uint8Array(data); + } + return data; + }; + var FS_getMode = (canRead, canWrite) => { var mode = 0; if (canRead) mode |= 292 | 73; @@ -1503,8 +1502,6 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { ignorePermissions:true, filesystems:null, syncFSRequests:0, - readFiles:{ - }, ErrnoError:class { name = 'ErrnoError'; // We set the `name` property to be able to identify `FS.ErrnoError` @@ -1770,9 +1767,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // return 0 if any user, group or owner bits are set. if (perms.includes('r') && !(node.mode & 292)) { return 2; - } else if (perms.includes('w') && !(node.mode & 146)) { + } + if (perms.includes('w') && !(node.mode & 146)) { return 2; - } else if (perms.includes('x') && !(node.mode & 73)) { + } + if (perms.includes('x') && !(node.mode & 73)) { return 2; } return 0; @@ -1813,10 +1812,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10; } - } else { - if (FS.isDir(node.mode)) { - return 31; - } + } else if (FS.isDir(node.mode)) { + return 31; } return 0; }, @@ -1826,13 +1823,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } if (FS.isLink(node.mode)) { return 32; - } else if (FS.isDir(node.mode)) { - if (FS.flagsToPermissionString(flags) !== 'r' // opening for write - || (flags & (512 | 64))) { // TODO: check for O_SEARCH? (== search for dir only) + } + var mode = FS.flagsToPermissionString(flags); + if (FS.isDir(node.mode)) { + // opening for write + // TODO: check for O_SEARCH? (== search for dir only) + if (mode !== 'r' || (flags & (512 | 64))) { return 31; } } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + return FS.nodePermissions(node, mode); }, checkOpExists(op, err) { if (!op) { @@ -2400,7 +2400,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (path === "") { throw new FS.ErrnoError(44); } - flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags; + flags = FS_modeStringToFlags(flags); if ((flags & 64)) { mode = (mode & 4095) | 32768; } else { @@ -2487,11 +2487,6 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (created) { FS.chmod(node, mode & 0o777); } - if (Module['logReadFiles'] && !(flags & 1)) { - if (!(path in FS.readFiles)) { - FS.readFiles[path] = 1; - } - } return stream; }, close(stream) { @@ -2638,14 +2633,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { writeFile(path, data, opts = {}) { opts.flags = opts.flags || 577; var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data == 'string') { - data = new Uint8Array(intArrayFromString(data, true)); - } - if (ArrayBuffer.isView(data)) { - FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); - } else { - abort('Unsupported data type'); - } + data = FS_fileDataToTypedArray(data); + FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); FS.close(stream); }, cwd:() => FS.currentPath, @@ -2865,11 +2854,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var mode = FS_getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { - if (typeof data == 'string') { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } + data = FS_fileDataToTypedArray(data); // make sure we can write to the file FS.chmod(node, mode | 146); var stream = FS.open(node, 577); @@ -4193,6 +4178,7 @@ var _sqlite3_status64, _sqlite3_bind_null, _sqlite3_bind_pointer, _sqlite3_bind_text, + _sqlite3_bind_zeroblob, _sqlite3_bind_parameter_count, _sqlite3_bind_parameter_name, _sqlite3_bind_parameter_index, @@ -4456,6 +4442,7 @@ function assignWasmExports(wasmExports) { _sqlite3_bind_null = Module['_sqlite3_bind_null'] = wasmExports['sqlite3_bind_null']; _sqlite3_bind_pointer = Module['_sqlite3_bind_pointer'] = wasmExports['sqlite3_bind_pointer']; _sqlite3_bind_text = Module['_sqlite3_bind_text'] = wasmExports['sqlite3_bind_text']; + _sqlite3_bind_zeroblob = Module['_sqlite3_bind_zeroblob'] = wasmExports['sqlite3_bind_zeroblob']; _sqlite3_bind_parameter_count = Module['_sqlite3_bind_parameter_count'] = wasmExports['sqlite3_bind_parameter_count']; _sqlite3_bind_parameter_name = Module['_sqlite3_bind_parameter_name'] = wasmExports['sqlite3_bind_parameter_name']; _sqlite3_bind_parameter_index = Module['_sqlite3_bind_parameter_index'] = wasmExports['sqlite3_bind_parameter_index']; @@ -4800,6 +4787,7 @@ Module.runSQLite3PostLoadInit = async function( - sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls - sqlite3-vfs-opfs.c-pp.js => OPFS VFS - sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS + - sqlite3-vfs-opfs-wl.c-pp.js => WebLock-using OPFS VFS - post-js-footer.js => this file's epilogue And all of that gets sandwiched between extern-pre-js.js and @@ -4834,11 +4822,11 @@ Module.runSQLite3PostLoadInit = async function( /* @preserve ** This code was built from sqlite3 version... ** -** SQLITE_VERSION "3.52.0" -** SQLITE_VERSION_NUMBER 3052000 -** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e" +** SQLITE_VERSION "3.53.0" +** SQLITE_VERSION_NUMBER 3053000 +** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** -** Emscripten SDK: 5.0.0 +** Emscripten SDK: 5.0.5 */ /* 2022-05-22 @@ -4941,6 +4929,13 @@ Module.runSQLite3PostLoadInit = async function( used in WASMFS-capable builds of the library (which the canonical builds do not include). + - `disable` (as of 3.53.0) may be an object with the following + properties: + - `vfs`, an object, may contain a map of VFS names to booleans. + Any mapping to falsy are disabled. The supported names + are: "kvvfs", "opfs", "opfs-sahpool", "opfs-wl". + - Other disabling options may be added in the future. + [^1] = This property may optionally be a function, in which case this function calls that function to fetch the value, enabling delayed evaluation. @@ -4987,7 +4982,8 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( ); return sqlite3ApiBootstrap.sqlite3; } - const config = Object.assign(Object.create(null),{ + const nu = (...obj)=>Object.assign(Object.create(null),...obj); + const config = nu({ exports: undefined, memory: undefined, bigIntEnabled: !!globalThis.BigInt64Array, @@ -5004,7 +5000,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( certain wasm.xWrap.resultAdapter()s. */ useStdAlloc: false - }, apiConfig || {}); + }, apiConfig); Object.assign(config, { allocExportName: config.useStdAlloc ? 'malloc' : 'sqlite3_malloc', @@ -5037,7 +5033,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( not documented are installed as 1-to-1 proxies for their C-side counterparts. */ - const capi = Object.create(null); + const capi = nu(); /** Holds state which are specific to the WASM-related infrastructure and glue code. @@ -5046,7 +5042,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( dynamically after the api object is fully constructed, so not all are documented in this file. */ - const wasm = Object.create(null); + const wasm = nu(); /** Internal helper for SQLite3Error ctor. */ const __rcStr = (rc)=>{ @@ -5594,6 +5590,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( toss: function(...args){throw new Error(args.join(' '))}, toss3, typedArrayPart: wasm.typedArrayPart, + nu, assert: function(arg,msg){ if( !arg ){ util.toss("Assertion failed:",msg); @@ -5850,7 +5847,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; }; } - const rc = Object.create(null), ov = [0,0]; + const rc = nu(), ov = [0,0]; let i = 0, k; while((k = capi.sqlite3_compileoption_get(i++))){ f._opt(k,ov); @@ -5858,7 +5855,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( } return f._result = rc; }else if(Array.isArray(optName)){ - const rc = Object.create(null); + const rc = nu(); optName.forEach((v)=>{ rc[v] = capi.sqlite3_compileoption_used(v); }); @@ -5909,7 +5906,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( The memory lives in the WASM heap and can be used with routines such as wasm.poke() and wasm.heap8u().slice(). */ - wasm.pstack = Object.assign(Object.create(null),{ + wasm.pstack = nu({ /** Sets the current pstack position to the given pointer. Results are undefined if the passed-in value did not come from @@ -6131,7 +6128,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( // sqlite3__wasm_init_wasmfs() is not available return this.dir = ""; } - }.bind(Object.create(null)); + }.bind(nu()); /** Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a @@ -6485,6 +6482,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: + case capi.SQLITE_DBCONFIG_FP_DIGITS: if( !this.ip ){ this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int', ['sqlite3*', 'int', 'int', '*']); @@ -6506,7 +6504,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( default: return capi.SQLITE_MISUSE; } - }.bind(Object.create(null)); + }.bind(nu()); /** Given a (sqlite3_value*), this function attempts to convert it @@ -6740,7 +6738,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( if(rc) return SQLite3Error.toss(rc,arguments[2]+"() failed with code "+rc); const pv = wasm.peekPtr(this.ptr); return pv ? capi.sqlite3_value_to_js( pv, true ) : undefined; - }.bind(Object.create(null)); + }.bind(nu()); /** A wrapper around sqlite3_preupdate_new() which fetches the @@ -6780,6 +6778,62 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap( 'sqlite3changeset_old'); }/*changeset/preupdate additions*/ + /** + EXPERIMENTAL. For tentative addition in 3.53.0. + + sqlite3_js_retry_busy(maxTimes,callback[,beforeRetry]) + + Calls the given _synchronous_ callback function. If that function + returns sqlite3.capi.SQLITE_BUSY _or_ throws an SQLite3Error + with a resultCode property of that value then it will suppress + that error and try again, up to the given maximum number of + times. If the callback returns any other value than that, + it is returned. If the maximum number of retries has been + reached, an SQLite3Error with a resultCode value of + sqlite3.capi.SQLITE_BUSY is thrown. If the callback throws any + exception other than the aforementioned BUSY exception, it is + propagated. If it throws a BUSY exception on its final attempt, + that is propagated as well. + + If the beforeRetry argument is given, it must be a _synchronous_ + function. It is called immediately before each retry of the + callback (not for the initial call), passed the attempt number + (so it starts with 2, not 1). If it throws, the exception is + handled as described above. Its result value is ignored. + + To effectively retry "forever", pass a negative maxTimes value, + with the caveat that there is no recovery from that unless the + beforeRetry() can figure out when to throw. + + TODO: an async variant of this. + */ + capi.sqlite3_js_retry_busy = function(maxTimes, callback, beforeRetry){ + for(let n = 1; n <= maxTimes; ++n){ + try{ + if( beforeRetry && n>1 ) beforeRetry(n); + const rc = callback(); + if( capi.SQLITE_BUSY===rc ){ + if( n===maxTimes ){ + throw new SQLite3Error(rc, [ + "sqlite3_js_retry_busy() max retry attempts (", + maxTimes, + ") reached." + ].join('')); + } + continue; + } + return rc; + }catch(e){ + if( n{}; const debug = sqlite3.__isUnderTest - ? (...args)=>sqlite3.config.debug("kvvfs:", ...args) + ? (...args)=>sqlite3.config.debug?.("kvvfs:", ...args) : noop; - const warn = (...args)=>sqlite3.config.warn("kvvfs:", ...args); - const error = (...args)=>sqlite3.config.error("kvvfs:", ...args); + const warn = (...args)=>sqlite3.config.warn?.("kvvfs:", ...args); + const error = (...args)=>sqlite3.config.error?.("kvvfs:", ...args); /** Implementation of JS's Storage interface for use as backing store @@ -16270,17 +16331,21 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ and recreating it whenever a property index might be invalidated. */ class KVVfsStorage { - #map; - #keys; - #getKeys(){return this.#keys ??= Object.keys(this.#map);} + #map = Object.create(null); + #keys = null; + #size = 0; constructor(){ this.clear(); } + #getKeys(){ + return this.#keys ??= Object.keys(this.#map); + } + key(n){ - const k = this.#getKeys(); - return n= this.#size) return null; + return this.#getKeys()[n]; } getItem(k){ @@ -16288,14 +16353,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } setItem(k,v){ - if( !hop(this.#map, k) ){ + if( !(k in this.#map) ){ + ++this.#size; this.#keys = null; } this.#map[k] = ''+v; } removeItem(k){ - if( delete this.#map[k] ){ + if( k in this.#map ){ + delete this.#map[k]; + --this.#size; this.#keys = null; } } @@ -16303,10 +16371,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ clear(){ this.#map = Object.create(null); this.#keys = null; + this.#size = 0; } get length() { - return this.#getKeys().length; + return this.#size; } }/*KVVfsStorage*/; @@ -17068,36 +17137,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }, - // We override xRead/xWrite only for logging/debugging. They - // should otherwise be disabled (it's faster that way). - xRead: function(pFile,pTgt,n,iOff64){ - cache.popError(); - try{ - if( kvvfs?.log?.xRead ){ - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xRead", n, iOff64, h); - } - return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64); - }catch(e){ - error("xRead",e); - return cache.setError(e); - } - }, - xWrite: function(pFile,pSrc,n,iOff64){ - cache.popError(); - try{ - if( kvvfs?.log?.xWrite ){ - const h = pFileHandles.get(pFile); - util.assert(h, "Missing KVVfsFile handle"); - debug("xWrite", n, iOff64, h); - } - return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64); - }catch(e){ - error("xWrite",e); - return cache.setError(e); - } - }, }/*.ioDb*/, @@ -17109,9 +17148,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }/*.ioJrnl*/ }/*methodOverrides*/; - debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, - kvvfsMethods, capi.sqlite3_file.structInfo, - KVVfsFile.structInfo); + try { util.assert( cache.buffer.n>1024*129, "Heap buffer is not large enough" /* Native is SQLITE_KVOS_SZ is 133073 as of this writing */ ); @@ -17200,7 +17237,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ limitation which has since been overcome, but removal of JsStorageDb.prototype.clearStorage() would be a backwards compatibility break, so this function permits wiping the storage for those two - cases even if they are opened. Use with case. + cases even if they are opened. Use with care. */ const sqlite3_js_kvvfs_clear = function callee(which){ if( ''===which ){ @@ -17875,7 +17912,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } return rc; }catch(e){ - return VT.xErrror('xConnect', e, capi.SQLITE_ERROR); + return VT.xError('xConnect', e, capi.SQLITE_ERROR); } }, xCreate: wasm.ptr.null, // eponymous only @@ -17974,7 +18011,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ })/*globalThis.sqlite3ApiBootstrap.initializers*/; /* - 2022-09-18 + 2026-03-04 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: @@ -17985,283 +18022,584 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ *********************************************************************** - This file holds the synchronous half of an sqlite3_vfs - implementation which proxies, in a synchronous fashion, the - asynchronous Origin-Private FileSystem (OPFS) APIs using a second - Worker, implemented in sqlite3-opfs-async-proxy.js. This file is - intended to be appended to the main sqlite3 JS deliverable somewhere - after sqlite3-api-oo1.js. + This file holds code shared by sqlite3-vfs-opfs{,-wl}.c-pp.js. It + creates a private/internal sqlite3.opfs namespace common to the two + and used (only) by them and the test framework. It is not part of + the public API. The library deletes sqlite3.opfs in its final + bootstrapping steps unless it's specifically told to keep them (for + testing purposes only) using an undocumented and unsupported + mechanism. */ -'use strict'; globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ -/** - installOpfsVfs() returns a Promise which, on success, installs an - sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs - which accept a VFS. It is intended to be called via - sqlite3ApiBootstrap.initializers or an equivalent mechanism. - - The installed VFS uses the Origin-Private FileSystem API for - all file storage. On error it is rejected with an exception - explaining the problem. Reasons for rejection include, but are - not limited to: + 'use strict'; + if( sqlite3.config.disable?.vfs?.opfs && + sqlite3.config.disable.vfs['opfs-vfs'] ){ + return; + } + const toss = sqlite3.util.toss, + capi = sqlite3.capi, + util = sqlite3.util, + wasm = sqlite3.wasm; - - The counterpart Worker (see below) could not be loaded. + /** + Generic utilities for working with OPFS. This will get filled out + by the Promise setup and, on success, installed as sqlite3.opfs. - - The environment does not support OPFS. That includes when - this function is called from the main window thread. + This is an internal/private namespace intended for use solely by + the OPFS VFSes and test code for them. The library bootstrapping + process removes this object in non-testing contexts. + */ + const opfsUtil = sqlite3.opfs = Object.create(null); - Significant notes and limitations: + /** + Returns true if _this_ thread has access to the OPFS APIs. + */ + opfsUtil.thisThreadHasOPFS = ()=>{ + return globalThis.FileSystemHandle && + globalThis.FileSystemDirectoryHandle && + globalThis.FileSystemFileHandle && + globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && + navigator?.storage?.getDirectory; + }; - - The OPFS features used here are only available in dedicated Worker - threads. This file tries to detect that case, resulting in a - rejected Promise if those features do not seem to be available. + /** + Must be called by the OPFS VFSes immediately after they determine + whether OPFS is available by calling + thisThreadHasOPFS(). Resolves to the OPFS storage root directory + and sets opfsUtil.rootDirectory to that value. + */ + opfsUtil.getRootDir = async function f(){ + return f.promise ??= navigator.storage.getDirectory().then(d=>{ + opfsUtil.rootDirectory = d; + return d; + }).catch(e=>{ + delete f.promise; + throw e; + }); + }; - - It requires the SharedArrayBuffer and Atomics classes, and the - former is only available if the HTTP server emits the so-called - COOP and COEP response headers. These features are required for - proxying OPFS's synchronous API via the synchronous interface - required by the sqlite3_vfs API. + /** + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd arg + is true, the result is returned as an array of path elements, + else an absolute path string is returned. + */ + opfsUtil.getResolvedPath = function(filename,splitIt){ + const p = new URL(filename, "file://irrelevant").pathname; + return splitIt ? p.split('/').filter((v)=>!!v) : p; + }; - - This function may only be called a single time. When called, this - function removes itself from the sqlite3 object. + /** + Takes the absolute path to a filesystem element. Returns an + array of [handleOfContainingDir, filename]. If the 2nd argument + is truthy then each directory element leading to the file is + created along the way. Throws if any creation or resolution + fails. + */ + opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false){ + const path = opfsUtil.getResolvedPath(absFilename, true); + const filename = path.pop(); + let dh = await opfsUtil.getRootDir(); + for(const dirName of path){ + if(dirName){ + dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); + } + } + return [dh, filename]; + }; - All arguments to this function are for internal/development purposes - only. They do not constitute a public API and may change at any - time. + /** + Creates the given directory name, recursively, in + the OPFS filesystem. Returns true if it succeeds or the + directory already exists, else false. + */ + opfsUtil.mkdir = async function(absDirName){ + try { + await opfsUtil.getDirForFilename(absDirName+"/filepart", true); + return true; + }catch(e){ + //sqlite3.config.warn("mkdir(",absDirName,") failed:",e); + return false; + } + }; - The argument may optionally be a plain object with the following - configuration options: + /** + Checks whether the given OPFS filesystem entry exists, + returning true if it does, false if it doesn't or if an + exception is intercepted while trying to make the + determination. + */ + opfsUtil.entryExists = async function(fsEntryName){ + try { + const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); + await dh.getFileHandle(fn); + return true; + }catch(e){ + return false; + } + }; - - proxyUri: name of the async proxy JS file. + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + opfsUtil.randomFilename = function f(len=16){ + if(!f._chars){ + f._chars = "abcdefghijklmnopqrstuvwxyz"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for( ; i < len; ++i){ + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(""); + /* + An alternative impl. with an unpredictable length + but much simpler: - - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables - logging of errors. 2 enables logging of warnings and errors. 3 - additionally enables debugging info. Logging is performed - via the sqlite3.config.{log|warn|error}() functions. + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + */ + }; - - sanityChecks (=false): if true, some basic sanity tests are run on - the OPFS VFS API after it's initialized, before the returned - Promise resolves. This is only intended for testing and - development of the VFS, not client-side use. + /** + Returns a promise which resolves to an object which represents + all files and directories in the OPFS tree. The top-most object + has two properties: `dirs` is an array of directory entries + (described below) and `files` is a list of file names for all + files in that directory. - On success, the Promise resolves to the top-most sqlite3 namespace - object and that object gets a new object installed in its - `opfs` property, containing several OPFS-specific utilities. -*/ -const installOpfsVfs = function callee(options){ - if(!globalThis.SharedArrayBuffer - || !globalThis.Atomics){ - return Promise.reject( - new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. "+ - "The server must emit the COOP/COEP response headers to enable those. "+ - "See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep") - ); - }else if('undefined'===typeof WorkerGlobalScope){ - return Promise.reject( - new Error("The OPFS sqlite3_vfs cannot run in the main thread "+ - "because it requires Atomics.wait().") - ); - }else if(!globalThis.FileSystemHandle || - !globalThis.FileSystemDirectoryHandle || - !globalThis.FileSystemFileHandle || - !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || - !navigator?.storage?.getDirectory){ - return Promise.reject( - new Error("Missing required OPFS APIs.") - ); - } - if(!options || 'object'!==typeof options){ - options = Object.create(null); - } - const urlParams = new URL(globalThis.location.href).searchParams; - if(urlParams.has('opfs-disable')){ - //sqlite3.config.warn('Explicitly not installing "opfs" VFS due to opfs-disable flag.'); - return Promise.resolve(sqlite3); - } - if(undefined===options.verbose){ - options.verbose = urlParams.has('opfs-verbose') - ? (+urlParams.get('opfs-verbose') || 2) : 1; - } - if(undefined===options.sanityChecks){ - options.sanityChecks = urlParams.has('opfs-sanity-check'); - } - if(undefined===options.proxyUri){ - options.proxyUri = callee.defaultProxyUri; - } + Traversal starts at sqlite3.opfs.rootDirectory. - //sqlite3.config.warn("OPFS options =",options,globalThis.location); + Each `dirs` entry is an object in this form: - if('function' === typeof options.proxyUri){ - options.proxyUri = options.proxyUri(); - } - const thePromise = new Promise(function(promiseResolve_, promiseReject_){ - const loggers = [ - sqlite3.config.error, - sqlite3.config.warn, - sqlite3.config.log - ]; - const logImpl = (level,...args)=>{ - if(options.verbose>level) loggers[level]("OPFS syncer:",...args); - }; - const log = (...args)=>logImpl(2, ...args); - const warn = (...args)=>logImpl(1, ...args); - const error = (...args)=>logImpl(0, ...args); - const toss = sqlite3.util.toss; - const capi = sqlite3.capi; - const util = sqlite3.util; - const wasm = sqlite3.wasm; - const sqlite3_vfs = capi.sqlite3_vfs; - const sqlite3_file = capi.sqlite3_file; - const sqlite3_io_methods = capi.sqlite3_io_methods; - /** - Generic utilities for working with OPFS. This will get filled out - by the Promise setup and, on success, installed as sqlite3.opfs. + ``` + { name: directoryName, + dirs: [...subdirs], + files: [...file names] + } + ``` - ACHTUNG: do not rely on these APIs in client code. They are - experimental and subject to change or removal as the - OPFS-specific sqlite3_vfs evolves. - */ - const opfsUtil = Object.create(null); + The `files` and `subdirs` entries are always set but may be + empty arrays. - /** - Returns true if _this_ thread has access to the OPFS APIs. - */ - const thisThreadHasOPFS = ()=>{ - return globalThis.FileSystemHandle && - globalThis.FileSystemDirectoryHandle && - globalThis.FileSystemFileHandle && - globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && - navigator?.storage?.getDirectory; - }; + The returned object has the same structure but its `name` is + an empty string. All returned objects are created with + Object.create(null), so have no prototype. - /** - Not part of the public API. Solely for internal/development - use. - */ - opfsUtil.metrics = { - dump: function(){ - let k, n = 0, t = 0, w = 0; - for(k in state.opIds){ - const m = metrics[k]; - n += m.count; - t += m.time; - w += m.wait; - m.avgTime = (m.count && m.time) ? (m.time / m.count) : 0; - m.avgWait = (m.count && m.wait) ? (m.wait / m.count) : 0; - } - sqlite3.config.log(globalThis.location.href, - "metrics for",globalThis.location.href,":",metrics, - "\nTotal of",n,"op(s) for",t, - "ms (incl. "+w+" ms of waiting on the async side)"); - sqlite3.config.log("Serialization metrics:",metrics.s11n); - W.postMessage({type:'opfs-async-metrics'}); - }, - reset: function(){ - let k; - const r = (m)=>(m.count = m.time = m.wait = 0); - for(k in state.opIds){ - r(metrics[k] = Object.create(null)); + Design note: the entries do not contain more information, + e.g. file sizes, because getting such info is not only + expensive but is subject to locking-related errors. + */ + opfsUtil.treeList = async function(){ + const doDir = async function callee(dirHandle,tgt){ + tgt.name = dirHandle.name; + tgt.dirs = []; + tgt.files = []; + for await (const handle of dirHandle.values()){ + if('directory' === handle.kind){ + const subDir = Object.create(null); + tgt.dirs.push(subDir); + await callee(handle, subDir); + }else{ + tgt.files.push(handle.name); } - let s = metrics.s11n = Object.create(null); - s = s.serialize = Object.create(null); - s.count = s.time = 0; - s = metrics.s11n.deserialize = Object.create(null); - s.count = s.time = 0; } - }/*metrics*/; - const opfsIoMethods = new sqlite3_io_methods(); - const opfsVfs = new sqlite3_vfs() - .addOnDispose( ()=>opfsIoMethods.dispose()); - let promiseWasRejected = undefined; - const promiseReject = (err)=>{ - promiseWasRejected = true; - opfsVfs.dispose(); - return promiseReject_(err); }; - const promiseResolve = ()=>{ - promiseWasRejected = false; - return promiseResolve_(sqlite3); + const root = Object.create(null); + const dir = await opfsUtil.getRootDir(); + await doDir(dir, root); + return root; + }; + + /** + Irrevocably deletes _all_ files in the current origin's OPFS. + Obviously, this must be used with great caution. It may throw + an exception if removal of anything fails (e.g. a file is + locked), but the precise conditions under which the underlying + APIs will throw are not documented (so we cannot tell you what + they are). + */ + opfsUtil.rmfr = async function(){ + const rd = await opfsUtil.getRootDir(); + const dir = rd, opt = {recurse: true}; + for await (const handle of dir.values()){ + dir.removeEntry(handle.name, opt); + } + }; + + /** + Deletes the given OPFS filesystem entry. As this environment + has no notion of "current directory", the given name must be an + absolute path. If the 2nd argument is truthy, deletion is + recursive (use with caution!). + + The returned Promise resolves to true if the deletion was + successful, else false (but...). The OPFS API reports the + reason for the failure only in human-readable form, not + exceptions which can be type-checked to determine the + failure. Because of that... + + If the final argument is truthy then this function will + propagate any exception on error, rather than returning false. + */ + opfsUtil.unlink = async function(fsEntryName, recursive = false, + throwOnError = false){ + try { + const [hDir, filenamePart] = + await opfsUtil.getDirForFilename(fsEntryName, false); + await hDir.removeEntry(filenamePart, {recursive}); + return true; + }catch(e){ + if(throwOnError){ + throw new Error("unlink(",arguments[0],") failed: "+e.message,{ + cause: e + }); + } + return false; + } + }; + + /** + Traverses the OPFS filesystem, calling a callback for each + entry. The argument may be either a callback function or an + options object with any of the following properties: + + - `callback`: function which gets called for each filesystem + entry. It gets passed 3 arguments: 1) the + FileSystemFileHandle or FileSystemDirectoryHandle of each + entry (noting that both are instanceof FileSystemHandle). 2) + the FileSystemDirectoryHandle of the parent directory. 3) the + current depth level, with 0 being at the top of the tree + relative to the starting directory. If the callback returns a + literal false, as opposed to any other falsy value, traversal + stops without an error. Any exceptions it throws are + propagated. Results are undefined if the callback manipulate + the filesystem (e.g. removing or adding entries) because the + how OPFS iterators behave in the face of such changes is + undocumented. + + - `recursive` [bool=true]: specifies whether to recurse into + subdirectories or not. Whether recursion is depth-first or + breadth-first is unspecified! + + - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] + specifies the starting directory. + + If this function is passed a function, it is assumed to be the + callback. + + Returns a promise because it has to (by virtue of being async) + but that promise has no specific meaning: the traversal it + performs is synchronous. The promise must be used to catch any + exceptions propagated by the callback, however. + */ + opfsUtil.traverse = async function(opt){ + const defaultOpt = { + recursive: true, + directory: await opfsUtil.getRootDir() }; - const W = - new Worker(new URL(options.proxyUri, import.meta.url)); - setTimeout(()=>{ - /* At attempt to work around a browser-specific quirk in which - the Worker load is failing in such a way that we neither - resolve nor reject it. This workaround gives that resolve/reject - a time limit and rejects if that timer expires. Discussion: - https://sqlite.org/forum/forumpost/a708c98dcb3ef */ - if(undefined===promiseWasRejected){ - promiseReject( - new Error("Timeout while waiting for OPFS async proxy worker.") - ); + if('function'===typeof opt){ + opt = {callback:opt}; + } + opt = Object.assign(defaultOpt, opt||{}); + const doDir = async function callee(dirHandle, depth){ + for await (const handle of dirHandle.values()){ + if(false === opt.callback(handle, dirHandle, depth)) return false; + else if(opt.recursive && 'directory' === handle.kind){ + if(false === await callee(handle, depth + 1)) break; + } } - }, 4000); - W._originalOnError = W.onerror /* will be restored later */; - W.onerror = function(err){ - // The error object doesn't contain any useful info when the - // failure is, e.g., that the remote script is 404. - error("Error initializing OPFS asyncer:",err); - promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); }; - const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; - const dVfs = pDVfs - ? new sqlite3_vfs(pDVfs) - : null /* dVfs will be null when sqlite3 is built with - SQLITE_OS_OTHER. */; + doDir(opt.directory, 0); + }; + + /** + Impl of opfsUtil.importDb() when it's given a function as its + second argument. + */ + const importDbChunked = async function(filename, callback){ + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + const hFile = await hDir.getFileHandle(fnamePart, {create:true}); + let sah = await hFile.createSyncAccessHandle(); + let nWrote = 0, chunk, checkedHeader = false, err = false; + try{ + sah.truncate(0); + while( undefined !== (chunk = await callback()) ){ + if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if( !checkedHeader && 0===nWrote && chunk.byteLength>=15 ){ + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, {at: nWrote}); + nWrote += chunk.byteLength; + } + if( nWrote < 512 || 0!==nWrote % 512 ){ + toss("Input size",nWrote,"is not correct for an SQLite database."); + } + if( !checkedHeader ){ + const header = new Uint8Array(20); + sah.read( header, {at: 0} ); + util.affirmDbHeader( header ); + } + sah.write(new Uint8Array([1,1]), {at: 18}/*force db out of WAL mode*/); + return nWrote; + }catch(e){ + await sah.close(); + sah = undefined; + await hDir.removeEntry( fnamePart ).catch(()=>{}); + throw e; + }finally { + if( sah ) await sah.close(); + } + }; + + /** + Asynchronously imports the given bytes (a byte array or + ArrayBuffer) into the given database file. + + Results are undefined if the given db name refers to an opened + db. + + If passed a function for its second argument, its behaviour + changes: imports its data in chunks fed to it by the given + callback function. It calls the callback (which may be async) + repeatedly, expecting either a Uint8Array or ArrayBuffer (to + denote new input) or undefined (to denote EOF). For so long as + the callback continues to return non-undefined, it will append + incoming data to the given VFS-hosted database file. When + called this way, the resolved value of the returned Promise is + the number of bytes written to the target file. + + It very specifically requires the input to be an SQLite3 + database and throws if that's not the case. It does so in + order to prevent this function from taking on a larger scope + than it is specifically intended to. i.e. we do not want it to + become a convenience for importing arbitrary files into OPFS. + + This routine rewrites the database header bytes in the output + file (not the input array) to force disabling of WAL mode. + + On error this throws and the state of the input file is + undefined (it depends on where the exception was triggered). + + On success, resolves to the number of bytes written. + */ + opfsUtil.importDb = async function(filename, bytes){ + if( bytes instanceof Function ){ + return importDbChunked(filename, bytes); + } + if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + util.affirmIsDb(bytes); + const n = bytes.byteLength; + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + let sah, err, nWrote = 0; + try { + const hFile = await hDir.getFileHandle(fnamePart, {create:true}); + sah = await hFile.createSyncAccessHandle(); + sah.truncate(0); + nWrote = sah.write(bytes, {at: 0}); + if(nWrote != n){ + toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); + } + sah.write(new Uint8Array([1,1]), {at: 18}) /* force db out of WAL mode */; + return nWrote; + }catch(e){ + if( sah ){ await sah.close(); sah = undefined; } + await hDir.removeEntry( fnamePart ).catch(()=>{}); + throw e; + }finally{ + if( sah ) await sah.close(); + } + }; + + /** + Checks for features required for OPFS VFSes and throws with a + descriptive error message if they're not found. This is intended + to be run as part of async VFS installation steps. + */ + opfsUtil.vfsInstallationFeatureCheck = function(vfsName){ + if( !globalThis.SharedArrayBuffer || !globalThis.Atomics ){ + toss("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics.", + "The server must emit the COOP/COEP response headers to enable those.", + "See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep"); + }else if( 'undefined'===typeof WorkerGlobalScope ){ + toss("The OPFS sqlite3_vfs cannot run in the main thread", + "because it requires Atomics.wait()."); + }else if( !globalThis.FileSystemHandle || + !globalThis.FileSystemDirectoryHandle || + !globalThis.FileSystemFileHandle?.prototype?.createSyncAccessHandle || + !navigator?.storage?.getDirectory ){ + toss("Missing required OPFS APIs."); + }else if( 'opfs-wl'===vfsName && !globalThis.Atomics.waitAsync ){ + toss('The',vfsName,'VFS requires Atomics.waitAsync(), which is not available.'); + } + }; + + /** + Must be called by the VFS's main installation routine and passed + the options object that function receives and a reference to that + function itself (we don't need this anymore). + + It throws if OPFS is not available. + + If it returns falsy, it detected that OPFS should be disabled, in + which case the callee should immediately return/resolve to the + sqlite3 object. + + Else it returns a new copy of the options object, fleshed out + with any missing defaults. The caller must: + + - Set up any local state they need. + + - Call opfsUtil.createVfsState(vfsName,opt), where opt is the + object returned by this function. + + - Set up any references they may need to state returned + by the previous step. + + - Call opfvs.bindVfs() + */ + opfsUtil.initOptions = function callee(vfsName, options){ + const urlParams = new URL(globalThis.location.href).searchParams; + if( urlParams.has(vfsName+'-disable') ){ + //sqlite3.config.warn('Explicitly not installing "opfs" VFS due to opfs-disable flag.'); + return; + } + try{ + opfsUtil.vfsInstallationFeatureCheck(vfsName); + }catch(e){ + return; + } + options = util.nu(options); + options.vfsName = vfsName; + options.verbose ??= urlParams.has('opfs-verbose') + ? +urlParams.get('opfs-verbose') : 1; + options.sanityChecks ??= urlParams.has('opfs-sanity-check'); + + if( !opfsUtil.proxyUri ){ + opfsUtil.proxyUri = "sqlite3-opfs-async-proxy.js"; + if( sqlite3.scriptInfo?.sqlite3Dir ){ + /* Doing this from one scope up, outside of this function, does + not work. */ + opfsUtil.proxyUri = ( + sqlite3.scriptInfo.sqlite3Dir + opfsUtil.proxyUri + ); + } + } + options.proxyUri ??= opfsUtil.proxyUri; + if('function' === typeof options.proxyUri){ + options.proxyUri = options.proxyUri(); + } + //sqlite3.config.warn("opfsUtil options =",JSON.stringify(options), 'urlParams =', urlParams); + return opfsUtil.options = options; + }; + + /** + Creates, populates, and returns the main state object used by the + "opfs" and "opfs-wl" VFSes, and transfered from those to their + async counterparts. + + The returned object's vfs property holds the fully-populated + capi.sqlite3_vfs instance, tagged with lots of extra state which + the current VFSes need to have exposed to them. + + After setting up any local state needed, the caller must call + theVfs.bindVfs(X,Y), where X is an object containing the + sqlite3_io_methods to override and Y is a callback which gets + triggered if init succeeds, before the final Promise decides + whether or not to reject. + + This object must, when it's passed to the async part, contain + only cloneable or sharable objects. After the worker's "inited" + message arrives, other types of data may be added to it. + */ + opfsUtil.createVfsState = function(){ + const state = util.nu(); + const options = opfsUtil.options; + state.verbose = options.verbose; + + const loggers = [ + sqlite3.config.error, + sqlite3.config.warn, + sqlite3.config.log + ]; + const vfsName = options.vfsName + || toss("Maintenance required: missing VFS name"); + const logImpl = (level,...args)=>{ + if(state.verbose>level) loggers[level](vfsName+":",...args); + }; + const log = (...args)=>logImpl(2, ...args), + warn = (...args)=>logImpl(1, ...args), + error = (...args)=>logImpl(0, ...args), + capi = sqlite3.capi, + wasm = sqlite3.wasm; + + const opfsVfs = state.vfs = new capi.sqlite3_vfs(); + const opfsIoMethods = opfsVfs.ioMethods = new capi.sqlite3_io_methods(); + opfsIoMethods.$iVersion = 1; opfsVfs.$iVersion = 2/*yes, two*/; opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; opfsVfs.$mxPathname = 1024/* sure, why not? The OPFS name length limit is undocumented/unspecified. */; - opfsVfs.$zName = wasm.allocCString("opfs"); - // All C-side memory of opfsVfs is zeroed out, but just to be explicit: - opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; + opfsVfs.$zName = wasm.allocCString(vfsName); opfsVfs.addOnDispose( - '$zName', opfsVfs.$zName, - 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null) + '$zName', opfsVfs.$zName, opfsIoMethods + /** + Pedantic sidebar: the entries in this array are items to + clean up when opfsVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown + order of the wasm engine and the JS one are undefined so + there is no guaranty that the opfsVfs instance would be + available in one environment or the other when + sqlite3_os_end() is called (_if_ it gets called at all in a + wasm build, which is undefined). i.e. addOnDispose() here is + a matter of "correctness", not necessity. It just wouldn't do + to leave the impression that we're blindly leaking memory. + */ ); - /** - Pedantic sidebar about opfsVfs.ondispose: the entries in that array - are items to clean up when opfsVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the opfsVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - /** - State which we send to the async-api Worker or share with it. - This object must initially contain only cloneable or sharable - objects. After the worker's "inited" message arrives, other types - of data may be added to it. - For purposes of Atomics.wait() and Atomics.notify(), we use a - SharedArrayBuffer with one slot reserved for each of the API - proxy's methods. The sync side of the API uses Atomics.wait() - on the corresponding slot and the async side uses - Atomics.notify() on that slot. - - The approach of using a single SAB to serialize comms for all - instances might(?) lead to deadlock situations in multi-db - cases. We should probably have one SAB here with a single slot - for locking a per-file initialization step and then allocate a - separate SAB like the above one for each file. That will - require a bit of acrobatics but should be feasible. The most - problematic part is that xOpen() would have to use - postMessage() to communicate its SharedArrayBuffer, and mixing - that approach with Atomics.wait/notify() gets a bit messy. - */ - const state = Object.create(null); - state.verbose = options.verbose; - state.littleEndian = (()=>{ - const buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true /* ==>littleEndian */); - // Int16Array uses the platform's endianness. - return new Int16Array(buffer)[0] === 256; - })(); + opfsVfs.metrics = util.nu({ + counters: util.nu(), + dump: function(){ + let k, n = 0, t = 0, w = 0; + for(k in state.opIds){ + const m = metrics[k]; + n += m.count; + t += m.time; + w += m.wait; + m.avgTime = (m.count && m.time) ? (m.time / m.count) : 0; + m.avgWait = (m.count && m.wait) ? (m.wait / m.count) : 0; + } + sqlite3.config.log(globalThis.location.href, + "metrics for",globalThis.location.href,":",metrics, + "\nTotal of",n,"op(s) for",t, + "ms (incl. "+w+" ms of waiting on the async side)"); + sqlite3.config.log("Serialization metrics:",opfsVfs.metrics.counters.s11n); + opfsVfs.worker?.postMessage?.({type:'opfs-async-metrics'}); + }, + reset: function(){ + let k; + const r = (m)=>(m.count = m.time = m.wait = 0); + const m = opfsVfs.metrics.counters; + for(k in state.opIds){ + r(m[k] = Object.create(null)); + } + let s = m.s11n = Object.create(null); + s = s.serialize = Object.create(null); + s.count = s.time = 0; + s = m.s11n.deserialize = Object.create(null); + s.count = s.time = 0; + } + })/*opfsVfs.metrics*/; + /** asyncIdleWaitTime is how long (ms) to wait, in the async proxy, for each Atomics.wait() when waiting on inbound VFS API calls. @@ -18282,7 +18620,8 @@ const installOpfsVfs = function callee(options){ 0 = no exception logging. 1 = only log exceptions for "significant" ops like xOpen(), - xRead(), and xWrite(). + xRead(), and xWrite(). Exceptions related to, e.g., wait/retry + loops in acquiring SyncAccessHandles are not logged. 2 = log all exceptions. */ @@ -18308,22 +18647,31 @@ const installOpfsVfs = function callee(options){ state.fileBufferSize/* file i/o block */ + state.sabS11nSize/* argument/result serialization block */ ); + + /** + For purposes of Atomics.wait() and Atomics.notify(), we use a + SharedArrayBuffer with one slot reserved for each of the API + proxy's methods. The sync side of the API uses Atomics.wait() + on the corresponding slot and the async side uses + Atomics.notify() on that slot. state.opIds holds the SAB slot + IDs of each of those. + */ state.opIds = Object.create(null); - const metrics = Object.create(null); { /* Indexes for use in our SharedArrayBuffer... */ let i = 0; /* SAB slot used to communicate which operation is desired between both workers. This worker writes to it and the other - listens for changes. */ + listens for changes and clears it. The values written to it + are state.opIds.x[A-Z][a-z]+, defined below.*/ state.opIds.whichOp = i++; - /* Slot for storing return values. This worker listens to that - slot and the other worker writes to it. */ + /* Slot for storing return values. This side listens to that + slot and the async proxy writes to it. */ state.opIds.rc = i++; - /* Each function gets an ID which this worker writes to - the whichOp slot. The async-api worker uses Atomic.wait() - on the whichOp slot to figure out which operation to run - next. */ + /* Each function gets an ID which this worker writes to the + state.opIds.whichOp slot. The async-api worker uses + Atomic.wait() on the whichOp slot to figure out which + operation to run next. */ state.opIds.xAccess = i++; state.opIds.xClose = i++; state.opIds.xDelete = i++; @@ -18337,24 +18685,28 @@ const installOpfsVfs = function callee(options){ state.opIds.xTruncate = i++; state.opIds.xUnlock = i++; state.opIds.xWrite = i++; - state.opIds.mkdir = i++; + state.opIds.mkdir = i++ /*currently unused*/; + /** Internal signals which are used only during development and + testing via the dev console. */ state.opIds['opfs-async-metrics'] = i++; state.opIds['opfs-async-shutdown'] = i++; /* The retry slot is used by the async part for wait-and-retry - semantics. Though we could hypothetically use the xSleep slot - for that, doing so might lead to undesired side effects. */ + semantics. It is never written to, only used as a convenient + place to wait-with-timeout for a value which will never be + written, i.e. sleep()ing, before retrying a failed attempt to + acquire a SharedAccessHandle. */ state.opIds.retry = i++; state.sabOP = new SharedArrayBuffer( - i * 4/* ==sizeof int32, noting that Atomics.wait() and friends - can only function on Int32Array views of an SAB. */); - opfsUtil.metrics.reset(); + i * 4/* 4==sizeof int32, noting that Atomics.wait() and + friends can only function on Int32Array views of an + SAB. */); } /** SQLITE_xxx constants to export to the async worker counterpart... */ state.sq3Codes = Object.create(null); - [ + for(const k of [ 'SQLITE_ACCESS_EXISTS', 'SQLITE_ACCESS_READWRITE', 'SQLITE_BUSY', @@ -18382,17 +18734,22 @@ const installOpfsVfs = function callee(options){ 'SQLITE_OPEN_CREATE', 'SQLITE_OPEN_DELETEONCLOSE', 'SQLITE_OPEN_MAIN_DB', - 'SQLITE_OPEN_READONLY' - ].forEach((k)=>{ - if(undefined === (state.sq3Codes[k] = capi[k])){ - toss("Maintenance required: not found:",k); - } - }); + 'SQLITE_OPEN_READONLY', + 'SQLITE_LOCK_NONE', + 'SQLITE_LOCK_SHARED', + 'SQLITE_LOCK_RESERVED', + 'SQLITE_LOCK_PENDING', + 'SQLITE_LOCK_EXCLUSIVE' + ]){ + state.sq3Codes[k] = + capi[k] ?? toss("Maintenance required: not found:",k); + } + state.opfsFlags = Object.assign(Object.create(null),{ /** Flag for use with xOpen(). URI flag "opfs-unlock-asap=1" enables this. See defaultUnlockAsap, below. - */ + */ OPFS_UNLOCK_ASAP: 0x01, /** Flag for use with xOpen(). URI flag "delete-before-open=1" @@ -18405,33 +18762,37 @@ const installOpfsVfs = function callee(options){ downstream errors. An unlink can fail if, e.g., another tab has the handle open. - It goes without saying that deleting a file out from under another - instance results in Undefined Behavior. + It goes without saying that deleting a file out from under + another instance results in Undefined Behavior. */ OPFS_UNLINK_BEFORE_OPEN: 0x02, /** - If true, any async routine which implicitly acquires a sync - access handle (i.e. an OPFS lock) will release that lock at - the end of the call which acquires it. If false, such - "autolocks" are not released until the VFS is idle for some - brief amount of time. - - The benefit of enabling this is much higher concurrency. The - down-side is much-reduced performance (as much as a 4x decrease - in speedtest1). + If true, any async routine which must implicitly acquire a + sync access handle (i.e. an OPFS lock), without an active + xLock(), will release that lock at the end of the call which + acquires it. If false, such implicit locks are not released + until the VFS is idle for some brief amount of time, as + defined by state.asyncIdleWaitTime. + + The benefit of enabling this is higher concurrency. The + down-side is much-reduced performance (as much as a 4x + decrease in speedtest1). */ defaultUnlockAsap: false }); + opfsVfs.metrics.reset()/*must not be called until state.opIds is set up*/; + const metrics = opfsVfs.metrics.counters; + /** Runs the given operation (by name) in the async worker counterpart, waits for its response, and returns the result - which the async worker writes to SAB[state.opIds.rc]. The - 2nd and subsequent arguments must be the arguments for the - async op. + which the async worker writes to SAB[state.opIds.rc]. The 2nd + and subsequent arguments must be the arguments for the async op + (see sqlite3-opfs-async-proxy.c-pp.js). */ - const opRun = (op,...args)=>{ - const opNdx = state.opIds[op] || toss("Invalid op ID:",op); + const opRun = opfsVfs.opRun = (op,...args)=>{ + const opNdx = state.opIds[op] || toss(opfsVfs.vfsName+": Invalid op ID:",op); state.s11n.serialize(...args); Atomics.store(state.sabOPView, state.opIds.rc, -1); Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx); @@ -18446,14 +18807,15 @@ const installOpfsVfs = function callee(options){ https://github.com/sqlite/sqlite-wasm/issues/12 Summary: in at least one browser flavor, under high loads, - the wait()/notify() pairings can get out of sync. Calling - wait() here until it returns 'not-equal' gets them back in - sync. + the wait()/notify() pairings can get out of sync and/or + spuriously wake up. Calling wait() here until it returns + 'not-equal' gets them back in sync. */ } /* When the above wait() call returns 'not-equal', the async - half will have completed the operation and reported its results - in the state.opIds.rc slot of the SAB. */ + half will have completed the operation and reported its + results in the state.opIds.rc slot of the SAB. It may have + also serialized an exception for us. */ const rc = Atomics.load(state.sabOPView, state.opIds.rc); metrics[op].wait += performance.now() - t; if(rc && state.asyncS11nExceptions){ @@ -18463,248 +18825,41 @@ const installOpfsVfs = function callee(options){ return rc; }; - /** - Not part of the public API. Only for test/development use. - */ - opfsUtil.debug = { - asyncShutdown: ()=>{ - warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); - opRun('opfs-async-shutdown'); - }, - asyncRestart: ()=>{ - warn("Attempting to restart OPFS VFS async listener. Might work, might not."); - W.postMessage({type: 'opfs-async-restart'}); - } - }; - - const initS11n = ()=>{ - /** - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ACHTUNG: this code is 100% duplicated in the other half of - this proxy! The documentation is maintained in the - "synchronous half". - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - This proxy de/serializes cross-thread function arguments and - output-pointer values via the state.sabIO SharedArrayBuffer, - using the region defined by (state.sabS11nOffset, - state.sabS11nOffset + state.sabS11nSize]. Only one dataset is - recorded at a time. - - This is not a general-purpose format. It only supports the - range of operations, and data sizes, needed by the - sqlite3_vfs and sqlite3_io_methods operations. Serialized - data are transient and this serialization algorithm may - change at any time. - - The data format can be succinctly summarized as: - - Nt...Td...D - - Where: - - - N = number of entries (1 byte) - - - t = type ID of first argument (1 byte) - - - ...T = type IDs of the 2nd and subsequent arguments (1 byte - each). - - - d = raw bytes of first argument (per-type size). - - - ...D = raw bytes of the 2nd and subsequent arguments (per-type - size). - - All types except strings have fixed sizes. Strings are stored - using their TextEncoder/TextDecoder representations. It would - arguably make more sense to store them as Int16Arrays of - their JS character values, but how best/fastest to get that - in and out of string form is an open point. Initial - experimentation with that approach did not gain us any speed. - - Historical note: this impl was initially about 1% this size by - using using JSON.stringify/parse(), but using fit-to-purpose - serialization saves considerable runtime. - */ - if(state.s11n) return state.s11n; - const textDecoder = new TextDecoder(), - textEncoder = new TextEncoder('utf-8'), - viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), - viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - state.s11n = Object.create(null); - /* Only arguments and return values of these types may be - serialized. This covers the whole range of types needed by the - sqlite3_vfs API. */ - const TypeIds = Object.create(null); - TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; - TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; - TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; - TypeIds.string = { id: 4 }; - - const getTypeId = (v)=>( - TypeIds[typeof v] - || toss("Maintenance required: this value type cannot be serialized.",v) - ); - const getTypeIdById = (tid)=>{ - switch(tid){ - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:",tid); - } - }; - - /** - Returns an array of the deserialized state stored by the most - recent serialize() operation (from this thread or the - counterpart thread), or null if the serialization buffer is - empty. If passed a truthy argument, the serialization buffer - is cleared after deserialization. - */ - state.s11n.deserialize = function(clear=false){ - ++metrics.s11n.deserialize.count; - const t = performance.now(); - const argc = viewU8[0]; - const rc = argc ? [] : null; - if(argc){ - const typeIds = []; - let offset = 1, i, n, v; - for(i = 0; i < argc; ++i, ++offset){ - typeIds.push(getTypeIdById(viewU8[offset])); - } - for(i = 0; i < argc; ++i){ - const t = typeIds[i]; - if(t.getter){ - v = viewDV[t.getter](offset, state.littleEndian); - offset += t.size; - }else{/*String*/ - n = viewDV.getInt32(offset, state.littleEndian); - offset += 4; - v = textDecoder.decode(viewU8.slice(offset, offset+n)); - offset += n; - } - rc.push(v); - } - } - if(clear) viewU8[0] = 0; - //log("deserialize:",argc, rc); - metrics.s11n.deserialize.time += performance.now() - t; - return rc; - }; - - /** - Serializes all arguments to the shared buffer for consumption - by the counterpart thread. - - This routine is only intended for serializing OPFS VFS - arguments and (in at least one special case) result values, - and the buffer is sized to be able to comfortably handle - those. - - If passed no arguments then it zeroes out the serialization - state. - */ - state.s11n.serialize = function(...args){ - const t = performance.now(); - ++metrics.s11n.serialize.count; - if(args.length){ - //log("serialize():",args); - const typeIds = []; - let i = 0, offset = 1; - viewU8[0] = args.length & 0xff /* header = # of args */; - for(; i < args.length; ++i, ++offset){ - /* Write the TypeIds.id value into the next args.length - bytes. */ - typeIds.push(getTypeId(args[i])); - viewU8[offset] = typeIds[i].id; - } - for(i = 0; i < args.length; ++i) { - /* Deserialize the following bytes based on their - corresponding TypeIds.id from the header. */ - const t = typeIds[i]; - if(t.setter){ - viewDV[t.setter](offset, args[i], state.littleEndian); - offset += t.size; - }else{/*String*/ - const s = textEncoder.encode(args[i]); - viewDV.setInt32(offset, s.byteLength, state.littleEndian); - offset += 4; - viewU8.set(s, offset); - offset += s.byteLength; - } - } - //log("serialize() result:",viewU8.slice(0,offset)); - }else{ - viewU8[0] = 0; - } - metrics.s11n.serialize.time += performance.now() - t; - }; - return state.s11n; - }/*initS11n()*/; - - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(""); - /* - An alternative impl. with an unpredictable length - but much simpler: - - Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) - */ - }; - - /** - Map of sqlite3_file pointers to objects constructed by xOpen(). - */ - const __openFiles = Object.create(null); - const opTimer = Object.create(null); opTimer.op = undefined; opTimer.start = undefined; - const mTimeStart = (op)=>{ + const mTimeStart = opfsVfs.mTimeStart = (op)=>{ opTimer.start = performance.now(); opTimer.op = op; ++metrics[op].count; }; - const mTimeEnd = ()=>( + const mTimeEnd = opfsVfs.mTimeEnd = ()=>( metrics[opTimer.op].time += performance.now() - opTimer.start ); + /** + Map of sqlite3_file pointers to objects constructed by xOpen(). + */ + const __openFiles = opfsVfs.__openFiles = Object.create(null); + /** Impls for the sqlite3_io_methods methods. Maintenance reminder: members are in alphabetical order to simplify finding them. */ - const ioSyncWrappers = { + const ioSyncWrappers = opfsVfs.ioSyncWrappers = util.nu({ xCheckReservedLock: function(pFile,pOut){ /** - As of late 2022, only a single lock can be held on an OPFS - file. We have no way of checking whether any _other_ db - connection has a lock except by trying to obtain and (on - success) release a sync-handle for it, but doing so would - involve an inherent race condition. For the time being, - pending a better solution, we simply report whether the - given pFile is open. - - Update 2024-06-12: based on forum discussions, this - function now always sets pOut to 0 (false): - - https://sqlite.org/forum/forumpost/a2f573b00cda1372 + After consultation with a topic expert: "opfs-wl" will + continue to use the same no-op impl which "opfs" does + because: + + - xCheckReservedLock() is just a hint. If SQLite needs to + lock, it's still going to try to lock. + + - We cannot do this check synchronously in "opfs-wl", + so would need to pass it to the async proxy. That would + make it inordinately expensive considering that it's + just a hint. */ wasm.poke(pOut, 0, 'i32'); return 0; @@ -18726,7 +18881,7 @@ const installOpfsVfs = function callee(options){ }, xFileControl: function(pFile, opId, pArg){ /*mTimeStart('xFileControl'); - mTimeEnd();*/ + mTimeEnd();*/ return capi.SQLITE_NOTFOUND; }, xFileSize: function(pFile,pSz64){ @@ -18744,25 +18899,8 @@ const installOpfsVfs = function callee(options){ mTimeEnd(); return rc; }, - xLock: function(pFile,lockType){ - mTimeStart('xLock'); - const f = __openFiles[pFile]; - let rc = 0; - /* All OPFS locks are exclusive locks. If xLock() has - previously succeeded, do nothing except record the lock - type. If no lock is active, have the async counterpart - lock the file. */ - if( !f.lockType ) { - rc = opRun('xLock', pFile, lockType); - if( 0===rc ) f.lockType = lockType; - }else{ - f.lockType = lockType; - } - mTimeEnd(); - return rc; - }, xRead: function(pFile,pDest,n,offset64){ - mTimeStart('xRead'); + mTimeStart('xRead'); const f = __openFiles[pFile]; let rc; try { @@ -18784,7 +18922,6 @@ const installOpfsVfs = function callee(options){ }, xSync: function(pFile,flags){ mTimeStart('xSync'); - ++metrics.xSync.count; const rc = opRun('xSync', pFile, flags); mTimeEnd(); return rc; @@ -18795,18 +18932,6 @@ const installOpfsVfs = function callee(options){ mTimeEnd(); return rc; }, - xUnlock: function(pFile,lockType){ - mTimeStart('xUnlock'); - const f = __openFiles[pFile]; - let rc = 0; - if( capi.SQLITE_LOCK_NONE === lockType - && f.lockType ){ - rc = opRun('xUnlock', pFile, lockType); - } - if( 0===rc ) f.lockType = lockType; - mTimeEnd(); - return rc; - }, xWrite: function(pFile,pSrc,n,offset64){ mTimeStart('xWrite'); const f = __openFiles[pFile]; @@ -18823,23 +18948,21 @@ const installOpfsVfs = function callee(options){ mTimeEnd(); return rc; } - }/*ioSyncWrappers*/; + })/*ioSyncWrappers*/; /** Impls for the sqlite3_vfs methods. Maintenance reminder: members are in alphabetical order to simplify finding them. */ - const vfsSyncWrappers = { + const vfsSyncWrappers = opfsVfs.vfsSyncWrappers = { xAccess: function(pVfs,zName,flags,pOut){ - mTimeStart('xAccess'); + mTimeStart('xAccess'); const rc = opRun('xAccess', wasm.cstrToJs(zName)); wasm.poke( pOut, (rc ? 0 : 1), 'i32' ); mTimeEnd(); return 0; }, xCurrentTime: function(pVfs,pOut){ - /* If it turns out that we need to adjust for timezone, see: - https://stackoverflow.com/a/11760121/1458521 */ wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000), 'double'); return 0; @@ -18863,18 +18986,22 @@ const installOpfsVfs = function callee(options){ /*CANTOPEN is required by the docs but SQLITE_RANGE would be a closer match*/; }, xGetLastError: function(pVfs,nOut,pOut){ - /* TODO: store exception.message values from the async - partner in a dedicated SharedArrayBuffer, noting that we'd have - to encode them... TextEncoder can do that for us. */ - warn("OPFS xGetLastError() has nothing sensible to return."); + /* Mutex use in the overlying APIs cause xGetLastError() to + not be terribly useful for us. e.g. it can't be used to + convey error messages from xOpen() because there would be a + race condition between sqlite3_open()'s call to xOpen() and + this function. */ + sqlite3.config.warn("OPFS xGetLastError() has nothing sensible to return."); return 0; }, //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ mTimeStart('xOpen'); let opfsFlags = 0; - if(0===zName){ - zName = randomFilename(); + let jzName, zToFree; + if( !zName ){ + jzName = opfsUtil.randomFilename(); + zName = zToFree = wasm.allocCString(jzName); }else if(wasm.isPtr(zName)){ if(capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)){ /* -----------------------^^^^^ MUST pass the untranslated @@ -18884,18 +19011,24 @@ const installOpfsVfs = function callee(options){ if(capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)){ opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN; } - zName = wasm.cstrToJs(zName); - //warn("xOpen zName =",zName, "opfsFlags =",opfsFlags); - } - const fh = Object.create(null); - fh.fid = pFile; - fh.filename = zName; - fh.sab = new SharedArrayBuffer(state.fileBufferSize); - fh.flags = flags; - fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) - && !!(flags & capi.SQLITE_OPEN_READONLY); - const rc = opRun('xOpen', pFile, zName, flags, opfsFlags); - if(!rc){ + jzName = wasm.cstrToJs(zName); + //sqlite3.config.warn("xOpen zName =",zName, "opfsFlags =",opfsFlags); + }else{ + sqlite3.config.error("Impossible zName value in xOpen?", zName); + return capi.SQLITE_CANTOPEN; + } + const fh = util.nu({ + fid: pFile, + filename: jzName, + sab: new SharedArrayBuffer(state.fileBufferSize), + flags: flags, + readOnly: !(capi.SQLITE_OPEN_CREATE & flags) + && !!(flags & capi.SQLITE_OPEN_READONLY) + }); + const rc = opRun('xOpen', pFile, jzName, flags, opfsFlags); + if(rc){ + if( zToFree ) wasm.dealloc(zToFree); + }else{ /* Recall that sqlite3_vfs::xClose() will be called, even on error, unless pFile->pMethods is NULL. */ if(fh.readOnly){ @@ -18903,7 +19036,8 @@ const installOpfsVfs = function callee(options){ } __openFiles[pFile] = fh; fh.sabView = state.sabFileBufView; - fh.sq3File = new sqlite3_file(pFile); + fh.sq3File = new capi.sqlite3_file(pFile); + if( zToFree ) fh.sq3File.addOnDispose(zToFree); fh.sq3File.$pMethods = opfsIoMethods.pointer; fh.lockType = capi.SQLITE_LOCK_NONE; } @@ -18912,515 +19046,569 @@ const installOpfsVfs = function callee(options){ }/*xOpen()*/ }/*vfsSyncWrappers*/; - if(dVfs){ - opfsVfs.$xRandomness = dVfs.$xRandomness; - opfsVfs.$xSleep = dVfs.$xSleep; - } - if(!opfsVfs.$xRandomness){ - /* If the default VFS has no xRandomness(), add a basic JS impl... */ - vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ - const heap = wasm.heap8u(); - let i = 0; - const npOut = Number(pOut); - for(; i < nOut; ++i) heap[npOut + i] = (Math.random()*255000) & 0xFF; - return i; - }; - } - if(!opfsVfs.$xSleep){ - /* If we can inherit an xSleep() impl from the default VFS then - assume it's sane and use it, otherwise install a JS-based - one. */ - vfsSyncWrappers.xSleep = function(pVfs,ms){ - Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); - return 0; - }; - } + const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; + if(pDVfs){ + const dVfs = new capi.sqlite3_vfs(pDVfs); + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + dVfs.dispose(); + } + if(!opfsVfs.$xRandomness){ + /* If the default VFS has no xRandomness(), add a basic JS impl... */ + opfsVfs.vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ + const heap = wasm.heap8u(); + let i = 0; + const npOut = Number(pOut); + for(; i < nOut; ++i) heap[npOut + i] = (Math.random()*255000) & 0xFF; + return i; + }; + } + if(!opfsVfs.$xSleep){ + /* If we can inherit an xSleep() impl from the default VFS then + assume it's sane and use it, otherwise install a JS-based + one. */ + opfsVfs.vfsSyncWrappers.xSleep = function(pVfs,ms){ + mTimeStart('xSleep'); + Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms); + mTimeEnd(); + return 0; + }; + } + +const initS11n = function(){ + /** + This proxy de/serializes cross-thread function arguments and + output-pointer values via the state.sabIO SharedArrayBuffer, + using the region defined by (state.sabS11nOffset, + state.sabS11nOffset + state.sabS11nSize]. Only one dataset is + recorded at a time. + + This is not a general-purpose format. It only supports the + range of operations, and data sizes, needed by the + sqlite3_vfs and sqlite3_io_methods operations. Serialized + data are transient and this serialization algorithm may + change at any time. + + The data format can be succinctly summarized as: + + Nt...Td...D + + Where: + + - N = number of entries (1 byte) + + - t = type ID of first argument (1 byte) + + - ...T = type IDs of the 2nd and subsequent arguments (1 byte + each). + + - d = raw bytes of first argument (per-type size). + + - ...D = raw bytes of the 2nd and subsequent arguments (per-type + size). + + All types except strings have fixed sizes. Strings are stored + using their TextEncoder/TextDecoder representations. It would + arguably make more sense to store them as Int16Arrays of + their JS character values, but how best/fastest to get that + in and out of string form is an open point. Initial + experimentation with that approach did not gain us any speed. + + Historical note: this impl was initially about 1% this size by + using using JSON.stringify/parse(), but using fit-to-purpose + serialization saves considerable runtime. + */ + if(state.s11n) return state.s11n; + const textDecoder = new TextDecoder(), + textEncoder = new TextEncoder('utf-8'), + viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), + viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + state.s11n = Object.create(null); + /* Only arguments and return values of these types may be + serialized. This covers the whole range of types needed by the + sqlite3_vfs API. */ + const TypeIds = Object.create(null); + TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; + TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; + TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; + TypeIds.string = { id: 4 }; + + const getTypeId = (v)=>( + TypeIds[typeof v] + || toss("Maintenance required: this value type cannot be serialized.",v) + ); + const getTypeIdById = (tid)=>{ + switch(tid){ + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:",tid); + } + }; + + /** + Returns an array of the deserialized state stored by the most + recent serialize() operation (from this thread or the + counterpart thread), or null if the serialization buffer is + empty. If passed a truthy argument, the serialization buffer + is cleared after deserialization. + */ + state.s11n.deserialize = function(clear=false){ + const t = performance.now(); + const argc = viewU8[0]; + const rc = argc ? [] : null; + if(argc){ + const typeIds = []; + let offset = 1, i, n, v; + for(i = 0; i < argc; ++i, ++offset){ + typeIds.push(getTypeIdById(viewU8[offset])); + } + for(i = 0; i < argc; ++i){ + const t = typeIds[i]; + if(t.getter){ + v = viewDV[t.getter](offset, state.littleEndian); + offset += t.size; + }else{/*String*/ + n = viewDV.getInt32(offset, state.littleEndian); + offset += 4; + v = textDecoder.decode(viewU8.slice(offset, offset+n)); + offset += n; + } + rc.push(v); + } + } + if(clear) viewU8[0] = 0; + //log("deserialize:",argc, rc); + return rc; + }; + + /** + Serializes all arguments to the shared buffer for consumption + by the counterpart thread. + + This routine is only intended for serializing OPFS VFS + arguments and (in at least one special case) result values, + and the buffer is sized to be able to comfortably handle + those. + + If passed no arguments then it zeroes out the serialization + state. + */ + state.s11n.serialize = function(...args){ + const t = performance.now(); + if(args.length){ + //log("serialize():",args); + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 0xff /* header = # of args */; + for(; i < args.length; ++i, ++offset){ + /* Write the TypeIds.id value into the next args.length + bytes. */ + typeIds.push(getTypeId(args[i])); + viewU8[offset] = typeIds[i].id; + } + for(i = 0; i < args.length; ++i) { + /* Deserialize the following bytes based on their + corresponding TypeIds.id from the header. */ + const t = typeIds[i]; + if(t.setter){ + viewDV[t.setter](offset, args[i], state.littleEndian); + offset += t.size; + }else{/*String*/ + const s = textEncoder.encode(args[i]); + viewDV.setInt32(offset, s.byteLength, state.littleEndian); + offset += 4; + viewU8.set(s, offset); + offset += s.byteLength; + } + } + //log("serialize() result:",viewU8.slice(0,offset)); + }else{ + viewU8[0] = 0; + } + }; + + + return state.s11n; +}/*initS11n()*/; + opfsVfs.initS11n = initS11n; + + /** + To be called by the VFS's main installation routine after it has + wired up enough state to provide its overridden io-method impls + (which must be properties of the ioMethods argument). Returns a + Promise which the installation routine must return. callback must + be a function which performs any post-bootstrap touchups, namely + plugging in a sqlite3.oo1 wrapper. It is passed (sqlite3, opfsVfs), + where opfsVfs is the sqlite3_vfs object which was set up by + opfsUtil.createVfsState(). + */ + opfsVfs.bindVfs = function(ioMethods, callback){ + Object.assign(opfsVfs.ioSyncWrappers, ioMethods); + const thePromise = new Promise(function(promiseResolve_, promiseReject_){ + let promiseWasRejected = undefined; + const promiseReject = (err)=>{ + promiseWasRejected = true; + opfsVfs.dispose(); + return promiseReject_(err); + }; + const promiseResolve = ()=>{ + try{ + callback(sqlite3, opfsVfs); + }catch(e){ + return promiseReject(e); + } + promiseWasRejected = false; + return promiseResolve_(sqlite3); + }; + const options = opfsUtil.options; + let proxyUri = options.proxyUri +( + (options.proxyUri.indexOf('?')<0) ? '?' : '&' + )+'vfs='+vfsName; + //sqlite3.config.error("proxyUri",options.proxyUri, (new Error())); + const W = opfsVfs.worker = + new Worker(new URL(proxyUri, import.meta.url)); + let zombieTimer = setTimeout(()=>{ + /* At attempt to work around a browser-specific quirk in which + the Worker load is failing in such a way that we neither + resolve nor reject it. This workaround gives that resolve/reject + a time limit and rejects if that timer expires. Discussion: + https://sqlite.org/forum/forumpost/a708c98dcb3ef */ + if(undefined===promiseWasRejected){ + promiseReject( + new Error("Timeout while waiting for OPFS async proxy worker.") + ); + } + }, 4000); + W._originalOnError = W.onerror /* will be restored later */; + W.onerror = function(err){ + // The error object doesn't contain any useful info when the + // failure is, e.g., that the remote script is 404. + error("Error initializing OPFS asyncer:",err); + promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); + }; + + const opRun = opfsVfs.opRun; + + const sanityCheck = function(){ + const scope = wasm.scopedAllocPush(); + const sq3File = new capi.sqlite3_file(); + try{ + const fid = sq3File.pointer; + const openFlags = capi.SQLITE_OPEN_CREATE + | capi.SQLITE_OPEN_READWRITE + //| capi.SQLITE_OPEN_DELETEONCLOSE + | capi.SQLITE_OPEN_MAIN_DB; + const pOut = wasm.scopedAlloc(8); + const dbFile = "/sanity/check/file"+randomFilename(8); + const zDbFile = wasm.scopedAllocCString(dbFile); + let rc; + state.s11n.serialize("This is ä string."); + rc = state.s11n.deserialize(); + log("deserialize() says:",rc); + if("This is ä string."!==rc[0]) toss("String d13n error."); + opfsVfs.vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut,'i32'); + log("xAccess(",dbFile,") exists ?=",rc); + rc = opfsVfs.vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, + fid, openFlags, pOut); + log("open rc =",rc,"state.sabOPView[xOpen] =", + state.sabOPView[state.opIds.xOpen]); + if(0!==rc){ + error("open failed with code",rc); + return; + } + opfsVfs.vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut,'i32'); + if(!rc) toss("xAccess() failed to detect file."); + rc = opfsVfs.ioSyncWrappers.xSync(sq3File.pointer, 0); + if(rc) toss('sync failed w/ rc',rc); + rc = opfsVfs.ioSyncWrappers.xTruncate(sq3File.pointer, 1024); + if(rc) toss('truncate failed w/ rc',rc); + wasm.poke(pOut,0,'i64'); + rc = opfsVfs.ioSyncWrappers.xFileSize(sq3File.pointer, pOut); + if(rc) toss('xFileSize failed w/ rc',rc); + log("xFileSize says:",wasm.peek(pOut, 'i64')); + rc = opfsVfs.ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); + if(rc) toss("xWrite() failed!"); + const readBuf = wasm.scopedAlloc(16); + rc = opfsVfs.ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); + wasm.poke(readBuf+6,0); + let jRead = wasm.cstrToJs(readBuf); + log("xRead() got:",jRead); + if("sanity"!==jRead) toss("Unexpected xRead() value."); + if(opfsVfs.vfsSyncWrappers.xSleep){ + log("xSleep()ing before close()ing..."); + opfsVfs.vfsSyncWrappers.xSleep(opfsVfs.pointer,2000); + log("waking up from xSleep()"); + } + rc = opfsVfs.ioSyncWrappers.xClose(fid); + log("xClose rc =",rc,"sabOPView =",state.sabOPView); + log("Deleting file:",dbFile); + opfsVfs.vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); + opfsVfs.vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.peek(pOut,'i32'); + if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); + warn("End of OPFS sanity checks."); + }finally{ + sq3File.dispose(); + wasm.scopedAllocPop(scope); + } + }/*sanityCheck()*/; + + W.onmessage = function({data}){ + //sqlite3.config.warn(vfsName,"Worker.onmessage:",data); + switch(data.type){ + case 'opfs-unavailable': + /* Async proxy has determined that OPFS is unavailable. There's + nothing more for us to do here. */ + promiseReject(new Error(data.payload.join(' '))); + break; + case 'opfs-async-loaded': + /* Arrives as soon as the asyc proxy finishes loading. + Pass our config and shared state on to the async + worker. */ + delete state.vfs; + W.postMessage({type: 'opfs-async-init', args: util.nu(state)}); + break; + case 'opfs-async-inited': { + /* Indicates that the async partner has received the 'init' + and has finished initializing, so the real work can + begin... */ + if(true===promiseWasRejected){ + break /* promise was already rejected via timer */; + } + clearTimeout(zombieTimer); + zombieTimer = null; + try { + sqlite3.vfs.installVfs({ + io: {struct: opfsVfs.ioMethods, methods: opfsVfs.ioSyncWrappers}, + vfs: {struct: opfsVfs, methods: opfsVfs.vfsSyncWrappers} + }); + state.sabOPView = new Int32Array(state.sabOP); + state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); + state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + opfsVfs.initS11n(); + delete opfsVfs.initS11n; + if(options.sanityChecks){ + warn("Running sanity checks because of opfs-sanity-check URL arg..."); + sanityCheck(); + } + if(opfsUtil.thisThreadHasOPFS()){ + opfsUtil.getRootDir().then((d)=>{ + W.onerror = W._originalOnError; + delete W._originalOnError; + log("End of OPFS sqlite3_vfs setup.", opfsVfs); + promiseResolve(); + }).catch(promiseReject); + }else{ + promiseResolve(); + } + }catch(e){ + error(e); + promiseReject(e); + } + break; + } + case 'debug': + warn("debug message from worker:",data); + break; + default: { + const errMsg = ( + "Unexpected message from the OPFS async worker: " + + JSON.stringify(data) + ); + error(errMsg); + promiseReject(new Error(errMsg)); + break; + } + }/*switch(data.type)*/ + }/*W.onmessage()*/; + })/*thePromise*/; + return thePromise; + }/*bindVfs()*/; + + return state; + }/*createVfsState()*/; + +}/*sqlite3ApiBootstrap.initializers*/); +/* + 2022-09-18 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This file holds the synchronous half of an sqlite3_vfs + implementation which proxies, in a synchronous fashion, the + asynchronous Origin-Private FileSystem (OPFS) APIs using a second + Worker, implemented in sqlite3-opfs-async-proxy.js. This file is + intended to be appended to the main sqlite3 JS deliverable somewhere + after sqlite3-api-oo1.js. +*/ +'use strict'; +globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ + if( !sqlite3.opfs || sqlite3.config.disable?.vfs?.opfs ){ + return; + } + const util = sqlite3.util, + opfsUtil = sqlite3.opfs || sqlite3.util.toss("Missing sqlite3.opfs"); + /** + installOpfsVfs() returns a Promise which, on success, installs an + sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs + which accept a VFS. It is intended to be called via + sqlite3ApiBootstrap.initializers or an equivalent mechanism. + + The installed VFS uses the Origin-Private FileSystem API for + all file storage. On error it is rejected with an exception + explaining the problem. Reasons for rejection include, but are + not limited to: - /** - Expects an OPFS file path. It gets resolved, such that ".." - components are properly expanded, and returned. If the 2nd arg - is true, the result is returned as an array of path elements, - else an absolute path string is returned. - */ - opfsUtil.getResolvedPath = function(filename,splitIt){ - const p = new URL(filename, "file://irrelevant").pathname; - return splitIt ? p.split('/').filter((v)=>!!v) : p; - }; + - The counterpart Worker (see below) could not be loaded. - /** - Takes the absolute path to a filesystem element. Returns an - array of [handleOfContainingDir, filename]. If the 2nd argument - is truthy then each directory element leading to the file is - created along the way. Throws if any creation or resolution - fails. - */ - opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false){ - const path = opfsUtil.getResolvedPath(absFilename, true); - const filename = path.pop(); - let dh = opfsUtil.rootDirectory; - for(const dirName of path){ - if(dirName){ - dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); - } - } - return [dh, filename]; - }; + - The environment does not support OPFS. That includes when + this function is called from the main window thread. - /** - Creates the given directory name, recursively, in - the OPFS filesystem. Returns true if it succeeds or the - directory already exists, else false. - */ - opfsUtil.mkdir = async function(absDirName){ - try { - await opfsUtil.getDirForFilename(absDirName+"/filepart", true); - return true; - }catch(e){ - //sqlite3.config.warn("mkdir(",absDirName,") failed:",e); - return false; - } - }; - /** - Checks whether the given OPFS filesystem entry exists, - returning true if it does, false if it doesn't or if an - exception is intercepted while trying to make the - determination. - */ - opfsUtil.entryExists = async function(fsEntryName){ - try { - const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName); - await dh.getFileHandle(fn); - return true; - }catch(e){ - return false; - } - }; + Significant notes and limitations: - /** - Generates a random ASCII string, intended for use as a - temporary file name. Its argument is the length of the string, - defaulting to 16. - */ - opfsUtil.randomFilename = randomFilename; + - The OPFS features used here are only available in dedicated Worker + threads. This file tries to detect that case, resulting in a + rejected Promise if those features do not seem to be available. - /** - Returns a promise which resolves to an object which represents - all files and directories in the OPFS tree. The top-most object - has two properties: `dirs` is an array of directory entries - (described below) and `files` is a list of file names for all - files in that directory. + - It requires the SharedArrayBuffer and Atomics classes, and the + former is only available if the HTTP server emits the so-called + COOP and COEP response headers. These features are required for + proxying OPFS's synchronous API via the synchronous interface + required by the sqlite3_vfs API. - Traversal starts at sqlite3.opfs.rootDirectory. + - This function may only be called a single time. When called, this + function removes itself from the sqlite3 object. - Each `dirs` entry is an object in this form: + All arguments to this function are for internal/development purposes + only. They do not constitute a public API and may change at any + time. - ``` - { name: directoryName, - dirs: [...subdirs], - files: [...file names] - } - ``` + The argument may optionally be a plain object with the following + configuration options: - The `files` and `subdirs` entries are always set but may be - empty arrays. + - proxyUri: name of the async proxy JS file or a synchronous function + which, when called, returns such a name. - The returned object has the same structure but its `name` is - an empty string. All returned objects are created with - Object.create(null), so have no prototype. + - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables + logging of errors. 2 enables logging of warnings and errors. 3 + additionally enables debugging info. Logging is performed + via the sqlite3.config.{log|warn|error}() functions. - Design note: the entries do not contain more information, - e.g. file sizes, because getting such info is not only - expensive but is subject to locking-related errors. - */ - opfsUtil.treeList = async function(){ - const doDir = async function callee(dirHandle,tgt){ - tgt.name = dirHandle.name; - tgt.dirs = []; - tgt.files = []; - for await (const handle of dirHandle.values()){ - if('directory' === handle.kind){ - const subDir = Object.create(null); - tgt.dirs.push(subDir); - await callee(handle, subDir); - }else{ - tgt.files.push(handle.name); - } - } - }; - const root = Object.create(null); - await doDir(opfsUtil.rootDirectory, root); - return root; - }; + - sanityChecks (=false): if true, some basic sanity tests are run on + the OPFS VFS API after it's initialized, before the returned + Promise resolves. This is only intended for testing and + development of the VFS, not client-side use. - /** - Irrevocably deletes _all_ files in the current origin's OPFS. - Obviously, this must be used with great caution. It may throw - an exception if removal of anything fails (e.g. a file is - locked), but the precise conditions under which the underlying - APIs will throw are not documented (so we cannot tell you what - they are). - */ - opfsUtil.rmfr = async function(){ - const dir = opfsUtil.rootDirectory, opt = {recurse: true}; - for await (const handle of dir.values()){ - dir.removeEntry(handle.name, opt); - } - }; + Additionaly, the (officially undocumented) 'opfs-disable' URL + argument will disable OPFS, making this function a no-op. - /** - Deletes the given OPFS filesystem entry. As this environment - has no notion of "current directory", the given name must be an - absolute path. If the 2nd argument is truthy, deletion is - recursive (use with caution!). - - The returned Promise resolves to true if the deletion was - successful, else false (but...). The OPFS API reports the - reason for the failure only in human-readable form, not - exceptions which can be type-checked to determine the - failure. Because of that... - - If the final argument is truthy then this function will - propagate any exception on error, rather than returning false. - */ - opfsUtil.unlink = async function(fsEntryName, recursive = false, - throwOnError = false){ - try { - const [hDir, filenamePart] = - await opfsUtil.getDirForFilename(fsEntryName, false); - await hDir.removeEntry(filenamePart, {recursive}); - return true; - }catch(e){ - if(throwOnError){ - throw new Error("unlink(",arguments[0],") failed: "+e.message,{ - cause: e - }); - } - return false; - } - }; + On success, the Promise resolves to the top-most sqlite3 namespace + object. Success does not necessarily mean that it installs the VFS, + as there are legitimate non-error reasons for OPFS not to be + available. +*/ +const installOpfsVfs = async function(options){ + options = opfsUtil.initOptions('opfs',options); + if( !options ) return sqlite3; + const capi = sqlite3.capi, + state = opfsUtil.createVfsState(), + opfsVfs = state.vfs, + metrics = opfsVfs.metrics.counters, + mTimeStart = opfsVfs.mTimeStart, + mTimeEnd = opfsVfs.mTimeEnd, + opRun = opfsVfs.opRun, + debug = (...args)=>sqlite3.config.debug("opfs:",...args), + warn = (...args)=>sqlite3.config.warn("opfs:",...args), + __openFiles = opfsVfs.__openFiles; + + //debug("options:",JSON.stringify(options)); + /* + At this point, createVfsState() has populated: - /** - Traverses the OPFS filesystem, calling a callback for each - entry. The argument may be either a callback function or an - options object with any of the following properties: - - - `callback`: function which gets called for each filesystem - entry. It gets passed 3 arguments: 1) the - FileSystemFileHandle or FileSystemDirectoryHandle of each - entry (noting that both are instanceof FileSystemHandle). 2) - the FileSystemDirectoryHandle of the parent directory. 3) the - current depth level, with 0 being at the top of the tree - relative to the starting directory. If the callback returns a - literal false, as opposed to any other falsy value, traversal - stops without an error. Any exceptions it throws are - propagated. Results are undefined if the callback manipulate - the filesystem (e.g. removing or adding entries) because the - how OPFS iterators behave in the face of such changes is - undocumented. - - - `recursive` [bool=true]: specifies whether to recurse into - subdirectories or not. Whether recursion is depth-first or - breadth-first is unspecified! - - - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] - specifies the starting directory. - - If this function is passed a function, it is assumed to be the - callback. - - Returns a promise because it has to (by virtue of being async) - but that promise has no specific meaning: the traversal it - performs is synchronous. The promise must be used to catch any - exceptions propagated by the callback, however. - */ - opfsUtil.traverse = async function(opt){ - const defaultOpt = { - recursive: true, - directory: opfsUtil.rootDirectory - }; - if('function'===typeof opt){ - opt = {callback:opt}; - } - opt = Object.assign(defaultOpt, opt||{}); - const doDir = async function callee(dirHandle, depth){ - for await (const handle of dirHandle.values()){ - if(false === opt.callback(handle, dirHandle, depth)) return false; - else if(opt.recursive && 'directory' === handle.kind){ - if(false === await callee(handle, depth + 1)) break; - } - } - }; - doDir(opt.directory, 0); - }; + - state: the configuration object we share with the async proxy. - /** - impl of importDb() when it's given a function as its second - argument. - */ - const importDbChunked = async function(filename, callback){ - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - const hFile = await hDir.getFileHandle(fnamePart, {create:true}); - let sah = await hFile.createSyncAccessHandle(); - let nWrote = 0, chunk, checkedHeader = false, err = false; - try{ - sah.truncate(0); - while( undefined !== (chunk = await callback()) ){ - if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); - if( !checkedHeader && 0===nWrote && chunk.byteLength>=15 ){ - util.affirmDbHeader(chunk); - checkedHeader = true; - } - sah.write(chunk, {at: nWrote}); - nWrote += chunk.byteLength; - } - if( nWrote < 512 || 0!==nWrote % 512 ){ - toss("Input size",nWrote,"is not correct for an SQLite database."); - } - if( !checkedHeader ){ - const header = new Uint8Array(20); - sah.read( header, {at: 0} ); - util.affirmDbHeader( header ); - } - sah.write(new Uint8Array([1,1]), {at: 18}/*force db out of WAL mode*/); - return nWrote; - }catch(e){ - await sah.close(); - sah = undefined; - await hDir.removeEntry( fnamePart ).catch(()=>{}); - throw e; - }finally { - if( sah ) await sah.close(); - } - }; + - opfsVfs: an sqlite3_vfs instance with lots of JS state attached + to it. - /** - Asynchronously imports the given bytes (a byte array or - ArrayBuffer) into the given database file. - - Results are undefined if the given db name refers to an opened - db. - - If passed a function for its second argument, its behaviour - changes: imports its data in chunks fed to it by the given - callback function. It calls the callback (which may be async) - repeatedly, expecting either a Uint8Array or ArrayBuffer (to - denote new input) or undefined (to denote EOF). For so long as - the callback continues to return non-undefined, it will append - incoming data to the given VFS-hosted database file. When - called this way, the resolved value of the returned Promise is - the number of bytes written to the target file. - - It very specifically requires the input to be an SQLite3 - database and throws if that's not the case. It does so in - order to prevent this function from taking on a larger scope - than it is specifically intended to. i.e. we do not want it to - become a convenience for importing arbitrary files into OPFS. - - This routine rewrites the database header bytes in the output - file (not the input array) to force disabling of WAL mode. - - On error this throws and the state of the input file is - undefined (it depends on where the exception was triggered). - - On success, resolves to the number of bytes written. - */ - opfsUtil.importDb = async function(filename, bytes){ - if( bytes instanceof Function ){ - return importDbChunked(filename, bytes); + with any code common to both the "opfs" and "opfs-wl" VFSes. Now + comes the VFS-dependent work... + */ + return opfsVfs.bindVfs(util.nu({ + xLock: function(pFile,lockType){ + mTimeStart('xLock'); + ++metrics.xLock.count; + const f = __openFiles[pFile]; + let rc = 0; + /* All OPFS locks are exclusive locks. If xLock() has + previously succeeded, do nothing except record the lock + type. If no lock is active, have the async counterpart + lock the file. */ + if( f.lockType ) { + f.lockType = lockType; + }else{ + rc = opRun('xLock', pFile, lockType); + if( 0===rc ) f.lockType = lockType; } - if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); - util.affirmIsDb(bytes); - const n = bytes.byteLength; - const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - let sah, err, nWrote = 0; - try { - const hFile = await hDir.getFileHandle(fnamePart, {create:true}); - sah = await hFile.createSyncAccessHandle(); - sah.truncate(0); - nWrote = sah.write(bytes, {at: 0}); - if(nWrote != n){ - toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); - } - sah.write(new Uint8Array([1,1]), {at: 18}) /* force db out of WAL mode */; - return nWrote; - }catch(e){ - if( sah ){ await sah.close(); sah = undefined; } - await hDir.removeEntry( fnamePart ).catch(()=>{}); - throw e; - }finally{ - if( sah ) await sah.close(); + mTimeEnd(); + return rc; + }, + xUnlock: function(pFile,lockType){ + mTimeStart('xUnlock'); + ++metrics.xUnlock.count; + const f = __openFiles[pFile]; + let rc = 0; + if( capi.SQLITE_LOCK_NONE === lockType + && f.lockType ){ + rc = opRun('xUnlock', pFile, lockType); } - }; - + if( 0===rc ) f.lockType = lockType; + mTimeEnd(); + return rc; + } + }), function(sqlite3, vfs){ + /* Post-VFS-registration initialization... */ if(sqlite3.oo1){ const OpfsDb = function(...args){ const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); - opt.vfs = opfsVfs.$zName; + opt.vfs = vfs.$zName; sqlite3.oo1.DB.dbCtorHelper.call(this, opt); }; OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); sqlite3.oo1.OpfsDb = OpfsDb; OpfsDb.importDb = opfsUtil.importDb; - sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback( - opfsVfs.pointer, - function(oo1Db, sqlite3){ - /* Set a relatively high default busy-timeout handler to - help OPFS dbs deal with multi-tab/multi-worker - contention. */ - sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); - } - ); - }/*extend sqlite3.oo1*/ - - const sanityCheck = function(){ - const scope = wasm.scopedAllocPush(); - const sq3File = new sqlite3_file(); - try{ - const fid = sq3File.pointer; - const openFlags = capi.SQLITE_OPEN_CREATE - | capi.SQLITE_OPEN_READWRITE - //| capi.SQLITE_OPEN_DELETEONCLOSE - | capi.SQLITE_OPEN_MAIN_DB; - const pOut = wasm.scopedAlloc(8); - const dbFile = "/sanity/check/file"+randomFilename(8); - const zDbFile = wasm.scopedAllocCString(dbFile); - let rc; - state.s11n.serialize("This is ä string."); - rc = state.s11n.deserialize(); - log("deserialize() says:",rc); - if("This is ä string."!==rc[0]) toss("String d13n error."); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut,'i32'); - log("xAccess(",dbFile,") exists ?=",rc); - rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, - fid, openFlags, pOut); - log("open rc =",rc,"state.sabOPView[xOpen] =", - state.sabOPView[state.opIds.xOpen]); - if(0!==rc){ - error("open failed with code",rc); - return; - } - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut,'i32'); - if(!rc) toss("xAccess() failed to detect file."); - rc = ioSyncWrappers.xSync(sq3File.pointer, 0); - if(rc) toss('sync failed w/ rc',rc); - rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); - if(rc) toss('truncate failed w/ rc',rc); - wasm.poke(pOut,0,'i64'); - rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); - if(rc) toss('xFileSize failed w/ rc',rc); - log("xFileSize says:",wasm.peek(pOut, 'i64')); - rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); - if(rc) toss("xWrite() failed!"); - const readBuf = wasm.scopedAlloc(16); - rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); - wasm.poke(readBuf+6,0); - let jRead = wasm.cstrToJs(readBuf); - log("xRead() got:",jRead); - if("sanity"!==jRead) toss("Unexpected xRead() value."); - if(vfsSyncWrappers.xSleep){ - log("xSleep()ing before close()ing..."); - vfsSyncWrappers.xSleep(opfsVfs.pointer,2000); - log("waking up from xSleep()"); - } - rc = ioSyncWrappers.xClose(fid); - log("xClose rc =",rc,"sabOPView =",state.sabOPView); - log("Deleting file:",dbFile); - vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.peek(pOut,'i32'); - if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); - warn("End of OPFS sanity checks."); - }finally{ - sq3File.dispose(); - wasm.scopedAllocPop(scope); - } - }/*sanityCheck()*/; - - W.onmessage = function({data}){ - //log("Worker.onmessage:",data); - switch(data.type){ - case 'opfs-unavailable': - /* Async proxy has determined that OPFS is unavailable. There's - nothing more for us to do here. */ - promiseReject(new Error(data.payload.join(' '))); - break; - case 'opfs-async-loaded': - /* Arrives as soon as the asyc proxy finishes loading. - Pass our config and shared state on to the async - worker. */ - W.postMessage({type: 'opfs-async-init',args: state}); - break; - case 'opfs-async-inited': { - /* Indicates that the async partner has received the 'init' - and has finished initializing, so the real work can - begin... */ - if(true===promiseWasRejected){ - break /* promise was already rejected via timer */; - } - try { - sqlite3.vfs.installVfs({ - io: {struct: opfsIoMethods, methods: ioSyncWrappers}, - vfs: {struct: opfsVfs, methods: vfsSyncWrappers} - }); - state.sabOPView = new Int32Array(state.sabOP); - state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize); - state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize); - initS11n(); - if(options.sanityChecks){ - warn("Running sanity checks because of opfs-sanity-check URL arg..."); - sanityCheck(); - } - if(thisThreadHasOPFS()){ - navigator.storage.getDirectory().then((d)=>{ - W.onerror = W._originalOnError; - delete W._originalOnError; - sqlite3.opfs = opfsUtil; - opfsUtil.rootDirectory = d; - log("End of OPFS sqlite3_vfs setup.", opfsVfs); - promiseResolve(); - }).catch(promiseReject); - }else{ - promiseResolve(); - } - }catch(e){ - error(e); - promiseReject(e); - } - break; + if( true ){ + /* 2026-03-06: this was a design mis-decision and is + inconsistent with sqlite3_open() and friends, but is + retained against the risk of introducing regressions if + it's removed. */ + sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback( + opfsVfs.pointer, + function(oo1Db, sqlite3){ + /* Set a relatively high default busy-timeout handler to + help OPFS dbs deal with multi-tab/multi-worker + contention. */ + sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); } - default: { - const errMsg = ( - "Unexpected message from the OPFS async worker: " + - JSON.stringify(data) - ); - error(errMsg); - promiseReject(new Error(errMsg)); - break; - } - }/*switch(data.type)*/ - }/*W.onmessage()*/; - })/*thePromise*/; - return thePromise; + ); + } + }/*extend sqlite3.oo1*/ + })/*bindVfs()*/; }/*installOpfsVfs()*/; -installOpfsVfs.defaultProxyUri = - "sqlite3-opfs-async-proxy.js"; globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ - try{ - let proxyJs = installOpfsVfs.defaultProxyUri; - if( sqlite3?.scriptInfo?.sqlite3Dir ){ - installOpfsVfs.defaultProxyUri = - sqlite3.scriptInfo.sqlite3Dir + proxyJs; - //sqlite3.config.warn("installOpfsVfs.defaultProxyUri =",installOpfsVfs.defaultProxyUri); - } - return installOpfsVfs().catch((e)=>{ - sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:",e.message); - }); - }catch(e){ - sqlite3.config.error("installOpfsVfs() exception:",e); - return Promise.reject(e); - } + return installOpfsVfs().catch((e)=>{ + sqlite3.config.warn("Ignoring inability to install 'opfs' sqlite3_vfs:",e); + }) }); }/*sqlite3ApiBootstrap.initializers.push()*/); /* @@ -19479,6 +19667,10 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'use strict'; + if( sqlite3.config.disable?.vfs?.['opfs-sahpool'] ){ + return; + } + const toss = sqlite3.util.toss; const toss3 = sqlite3.util.toss3; const initPromises = Object.create(null) /* cache of (name:result) of VFS init results */; @@ -20886,6 +21078,132 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }); }/*installOpfsSAHPoolVfs()*/; }/*sqlite3ApiBootstrap.initializers*/); +/* + 2026-02-20 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This file is a reimplementation of the "opfs" VFS (as distinct from + "opfs-sahpool") which uses WebLocks for locking instead of a bespoke + Atomics.wait()/notify() protocol. This file holds the "synchronous + half" of the VFS, whereas it shares the "asynchronous half" with the + "opfs" VFS. + + Testing has failed to show any genuine functional difference between + these VFSes other than "opfs-wl" being able to dole out xLock() + requests in a strictly FIFO manner by virtue of WebLocks being + globally managed by the browser. This tends to lead to, but does not + guaranty, fairer distribution of locks. Differences are unlikely to + be noticed except, perhaps, under very high contention. + + This file is intended to be appended to the main sqlite3 JS + deliverable somewhere after opfs-common-shared.c-pp.js. +*/ +'use strict'; +globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ + if( !sqlite3.opfs || sqlite3.config.disable?.vfs?.['opfs-wl'] ){ + return; + } + const util = sqlite3.util, + toss = sqlite3.util.toss; + const opfsUtil = sqlite3.opfs; + const vfsName = 'opfs-wl'; +/** + installOpfsWlVfs() returns a Promise which, on success, installs an + sqlite3_vfs named "opfs-wl", suitable for use with all sqlite3 APIs + which accept a VFS. It is intended to be called via + sqlite3ApiBootstrap.initializers or an equivalent mechanism. + + This VFS is essentially identical to the "opfs" VFS but uses + WebLocks for its xLock() and xUnlock() implementations. + + Quirks specific to this VFS: + + - The (officially undocumented) 'opfs-wl-disable' URL + argument will disable OPFS, making this function a no-op. + + Aside from locking differences in the VFSes, this function + otherwise behaves the same as + sqlite3-vfs-opfs.c-pp.js:installOpfsVfs(). +*/ +const installOpfsWlVfs = async function(options){ + options = opfsUtil.initOptions(vfsName,options); + if( !options ) return sqlite3; + const capi = sqlite3.capi, + state = opfsUtil.createVfsState(), + opfsVfs = state.vfs, + metrics = opfsVfs.metrics.counters, + mTimeStart = opfsVfs.mTimeStart, + mTimeEnd = opfsVfs.mTimeEnd, + opRun = opfsVfs.opRun, + debug = (...args)=>sqlite3.config.debug(vfsName+":",...args), + warn = (...args)=>sqlite3.config.warn(vfsName+":",...args), + __openFiles = opfsVfs.__openFiles; + + //debug("state",JSON.stringify(options)); + /* + At this point, createVfsState() has populated: + + - state: the configuration object we share with the async proxy. + + - opfsVfs: an sqlite3_vfs instance with lots of JS state attached + to it. + + with any code common to both the "opfs" and "opfs-wl" VFSes. Now + comes the VFS-dependent work... + */ + return opfsVfs.bindVfs(util.nu({ + xLock: function(pFile,lockType){ + mTimeStart('xLock'); + //debug("xLock()..."); + const f = __openFiles[pFile]; + const rc = opRun('xLock', pFile, lockType); + if( !rc ) f.lockType = lockType; + mTimeEnd(); + return rc; + }, + xUnlock: function(pFile,lockType){ + mTimeStart('xUnlock'); + const f = __openFiles[pFile]; + const rc = opRun('xUnlock', pFile, lockType); + if( !rc ) f.lockType = lockType; + mTimeEnd(); + return rc; + } + }), function(sqlite3, vfs){ + /* Post-VFS-registration initialization... */ + if(sqlite3.oo1){ + const OpfsWlDb = function(...args){ + const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); + opt.vfs = vfs.$zName; + sqlite3.oo1.DB.dbCtorHelper.call(this, opt); + }; + OpfsWlDb.prototype = Object.create(sqlite3.oo1.DB.prototype); + sqlite3.oo1.OpfsWlDb = OpfsWlDb; + OpfsWlDb.importDb = opfsUtil.importDb; + /* The "opfs" VFS variant adds a + oo1.DB.dbCtorHelper.setVfsPostOpenCallback() callback to set + a high busy_timeout. That was a design mis-decision and is + inconsistent with sqlite3_open() and friends, but is retained + against the risk of introducing regressions if it's removed. + This variant does not repeat that mistake. + */ + } + })/*bindVfs()*/; +}/*installOpfsWlVfs()*/; +globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{ + return installOpfsWlVfs().catch((e)=>{ + sqlite3.config.warn("Ignoring inability to install the",vfsName,"sqlite3_vfs:",e); + }); +}); +}/*sqlite3ApiBootstrap.initializers.push()*/); /* 2022-07-22 diff --git a/src/bin/sqlite3.wasm b/src/bin/sqlite3.wasm index 28b092090992a3db2b84c5e66094629503135b07..c7336e9c5e2ddfaf627490d959f0c1f9173dd804 100644 GIT binary patch delta 294835 zcmb5X2Y3|K`aeEr+P2x<$?m3*I=i&c6F|C}3B9OTuI1{zF+f0)Kp+GK4QwdV6e$Od z1qDO_6{J~EicwKOET|}`sGz7=P*lV&zt4MSH$m_B`9IHpd9pim>U-Y)zUR#F){d^H zKJDt2N+*qDk|eQrmUyr5XmR>T*Go&rv}x@YH~3=l>y`L)`jK%n9XIjmxKUAt!X;0) z(^Ztjr6tGy%LlNXvFT_#{dyVSwb$$DPxtZbF*e#u)Vp?T3pv7Ao2@;S(~M>Cj#>e0 z!%MWTtOZ}Dbz`Y~zxJMfJn~7z@8^%|nQi=Cmqe0SJqN_Gk5cV-<6_yb(agBHP(s`ad%`9KGofaKj9vT$^UR$>3T%Q@QT-~ z>Mo5*VJXum%kC83(Gv}|)l3BwYw14Bswf^$1~2ub$r-79s;4XcZ1m)K)7!ZeUDp&P zmA~t0(aMk&45Q+o_#)6Wj}c}bS$8R3!ho(ZgInI_Bhn=)-7Tp=jx0;eWN84NX}aW7 z7*m0*u*zG!R+VO|H z-F>aYs_bPR6s!CTZ=VYcZ=S4STA&fXa-O7lCAX}qps1oK5>}#Vm@^fKPBCOva;cVL zsBT?mMo+iFulHpD{;9qWQG;kIxg}i#3ME;yR22)6m}*FFrpcWK8VW^2OxGnq=>}c_ zHb68qElj-S2A3J`2u5=;Hx`CoZrKf*8bGcj>#EhCx!qo1&KpH%gD@z0B~_O(An~OO zeKZwa0X&uuQoAvWfhre9(hO5qK^cR;XEg7d7W2h4pvY9*Azxl=x7V$;cL6HEq+UTEvaPR3n8+X`q2yGZX z1e9mFv)qQI$p-Hk%)iJ0n%r)Pr0l{v(bv@FRIe^eF5RuuqP#xEqcN`{Uw#F+-v`ut zo9S-mH4NDRQ!8F{(pAWwu0;6bK`YWaNBfsI#VZLgnc~XmU68>)3Fa#qu^9K8z2a`! z>+*uv0s%QYCk_6cMMOzEmXZf@YbtJ~xUng=8qQo{n-&4G_rjuz3G z4>zE_p9S$@fj}z|K0iOGDEWRraL~fv0=h*C`XL_Za(*D72T%-HCBEOx>0-ziXw4xF zEDS(^Aga=W?AoHD>l7ALH8w%|O~o?f%sx7|z~@@lbA13*vTEs;@VVt9 zs;Vl>O27<&n3DOc4N#2jF zs+>G-e974HHQl;OOPN1eUR^e+tVZHZ9m}Sajg*$L#>F*tldBdf;OR996>ck@Nzw5uOyjcZB_R8uykM%wtl#ZxDgNt;+830GNl^|(o6q%BPi zM*-60`k&%|hMQe`wh77`ODiXrm5ivY8X?V;8>dW^POT}MB+ZcZ)-g#*QB)E&Jzuqb5&~?)>LaqpQoxfWl6xY3v?8AT>9>T+U1m2$D^%tSJG8s;W!I zSB)zhCq2*vX`(iZf9KDV2+x@p2hIW2q5|Fm}aWu4r)5vPgf<0n^EN{=+HGEqFC z3O&HekI8BEA6;yuWl4`Wkt5N@;Q%?Em!_r5(v!R-Et@^XYtypRwl(2qGKgI=smLDQ(ggI&qexD4?y}|EDPiuCd z34aN0A0c?}O};ZdquE>kQ!7|QdYgZnp2^ak<6vhmOq z(tAxM9|C$N3*v$!9#fSuXn=*Uhp4W!V(zMAImvMwEbhqac1?^9ju|27S}S zNRw*D)s&2#R5oQoW$D=Q(#a;IiJD~jw@rhI@)46KO_jdm2bx8$KGlQ@Q8lTiv}W=o z>3i9iStmXz#7#Nf`E9Y8}F{IG8{;jBT zY(<&$R8#-b$u(6aVCJ&%lg3t!m!4K~l0b|s9Y3x>*1bwpHk=sN6N5u_gBP*oCN~7S6tQt3=5(Zj&zX>yyV@KRrRxL38fzqg9_~GnO z>XD|NlWv$?Gpg#Q@zRIsF8kbBEZ;50?0FZ{;bDEpP) zotxd^wccOAc>BtJFeik?L znx)TH8@ZHtIN>hb&WrNWBMa3=8AAEUs!?Uq9lRnhYv3aFT>ksu#MfwDHKArK|I2cZd0uT~4nDOCc)2zc$SPBJ9@PA zVk4E3#icbhr6X^U-uj;kM{P*&@^R4&c9_qJ=H|TLh___#1dn6F5BT#@tIy|Zc0#)( zTTCde8F>Sd9ON4(eW51R@jvUO6FeuDk^SZWG=MQ0TU|D?M*50h8f(VB<`uCV_6?sO z%VQ@oXLcWQu)l4jS|daxVAVC!ca2#3M-4H;_nbA)Wj}DMd34Z^jTmTZIG((Y(~YQX zs;HVE{qcVkPpFXoLrapZX{ga-6Q21@Eu9y( zfQvPYcWRLb%X3MKY&I9)nOYZViI&DMZIPx*OSN2FWbmCWTnZQ0&G^w4y0T2mLV5bq zmKwX0TP;(y@nttjcj2lTtLMF1X0zq|s+PIR1L)GHWiyvUjurgwmf6`WwIorRph8$M zva|*~f1qU^c>Yw&_G~lHX_cjH(Xx47t5iO`m0Q`WWs2(wt#suH3{2&BwDKxXCdxOV z{3)#&x7(&!lR?3!8z(9$xd|D#lCtrW$CZ>%g7MhKziXA%5-Sm`Bzi0hn5(#!vx2WS{Qc1+1wX%AnVpUL*}$MT!y?9&?6 zhXc*T(eQz4sw$v=UctOMY(Ku!+-2j1E_;>dw8>Up6L<*oOWL@U*MT3qZJ1ZKakDr0 zg>6&0*;eC=+Gy-R@uE<8z+q)y}x*W_5S8P>#g(6 z@O546yVtke_n7Z#-!r~lzSn(!`eqxOjV;DE#>d7{`3cgjmCU!;7Q@>R-j zDRV-frQ995KeRHmCA2uaDEyat(@Zvt&1Q4hTsDu*XA9VEadta9qRrLk=^tvxv`@6- z+UMFA+6nC=?Q88D?K|yz?Fa3Q_KWtb_M7&*_J{Ua_;C2H$cvGMk;Rd-;q8$hBfp2= zi~Omrj_i#55xy_7IkF}4m$oCaDson<(`V>2^;!C@`fPoUK3`v;-=^QLFVyeQ7wL=j zCHhjG>+xm!o%&sRy?(cTuYRAtT)$s`KwqJ+)K}@N^@sF_^)>oheVzV@{;2+#zFyy; zKdx`oH|d-85A`qf6Z)6>SNhlb_wFCuKe|u5e{!F3|Lp$7{j2*o_wVjH&kWB@&n(Yu z&m7NO&pgk3&jQbFp4&YOJ$HB(d6szUJ@6|EzyTU}j)eV0K_m zU~XVuU_s!v!0myBfja_=0*eDn0!srtuq<$A;I2S@;O@XZf%^i>1NR3W2&@RK46F*Q z4m=ciIIt$LHn1-6NZ`@HY@NRfdU}ogK@R9Jx;bY;i!p}wCjeHYsh#ZQ15dJ9qY50Z6-oU=V zD}nuiR|D?_js!jod>l9y_#$v3@Fg$p7>)lP_#^OV;IF{hKwWS~aAt5;a87V;aDMQ% z;KJad;Nswt;L_l|!TW6BaBpyb@U`IU!QH_F!MB4S1wRRX8ay8SEckiw zi+J$M;8($~gC~RE1-}pe7@T3=YR)kinM=%#<|cEqxy5|Ke9C;reAe7ytIu<$+`YQBw=w#^I(2t?hp`Su$LO+Lo3H=&c8s_0;;XA{3h3mt2 zhwlmB8@?~RJbZunf$)m(%J8c2gW=WThr(;ZYs2fq8^Vu=H-PmnS< z>m$CPWG{YPw=Q-4_f0qkn+}+xx4gGBDQs~@8z5QZtwa9W)(@kA6 zwzle;DZ4&+mTC6(-?t9z_5p*XY~G`}>(%cii(%S}dYnf+XZL8Azgw|TmM^=_1^`A^ z8D=-(zz`cNyIC;8c)W$<^(dc}MTXbd@D zw#)^5X|I;>AGh_oqGf}dS~7EQVmQg{njANAVVb*nVefu&oy>3Qy%jUG>(e?kbr{wp zS&AjwY6b9VuH~cp^p{_GnLpO&s@$WG>rtklL4N{Tin0FHe&!5onlE$f{G9mVmo(J) zC7W;xE7nau%v8qIlx*{2%k#Fdyvbc!+i8)rmT9+SJt z$H7xk$#%fDkBF*jf)w!3NC%ny%SK|$BNDjMuDeaQJ!X-8YN2ii@q1vYZhP^2^iJJw zf!}_oPH6Twl1VE%(Fnn50cA|A+qRv%C>C)_lv@(Pk1V~2qO!gFSWqBe$(I|jIKmmxO3I31 zFv(tjEJ&kD_BymnV0y|#2o}qVDOB!_s`lPvSwUN(y9Trp7f+y{xY%@z54^CY+;EJK zzc44hEm7dYYaY!`YQPklA!F)g<26`gNQBfuo5>yM&X9<|?C!nG5hXQ920NP%^ zo42-e2c2rr%43Y=!n8kK&CHi0{muTFsAuI-$zVR8pw!C!f^wPmseKw)apqUdygNdG zpWdh0^XZN+v@=>SKkO(x$s8aTK^4h0~%vH+yylZ%prVUDZ(AeF_|$JA+r+w+JmuR?1O z7Gr^Vi0VAvH!Th@6I{U(I)h|H?(Y{wiIIM4Boqw=iU9kB$Zg`rVuNX1KtY8*LCGZH zM8g)q>JicOBEL*mqM;#!qHFDSY&TEsHz|&_#-Lf-)7+ZH*2a{k1;=C{T<~loMwEXb zfW!$1uw+ZCKv!uPu$Wl3p1_KwY>2UGL?ee5F_(iSm&K-8|Ix34QUk_hdy|7_2?zrY zK*7edx6!MtQ&q^OC0YPm1!w_NrCh!(4kX5+F)c{EL_fAfrNGzt%L?kM*9gn&pDk1zC_)EuM z0DlqudGVKuKcD4~>im`gH*^h&PKNHYQllPAiFyq$Dba}Grk{Y}qMvls+wG%o6RQCTUn)CTlbdG4UhxPmrOoHU@-Dux7C=l^>y zpB*RdTmgvd@R>3o%VM~e24zLvSiX!=NY%*>n6iBqAJ@bv>_0TGM35+0k?NxvE28q{ z0#`sg)DPj?@A530SSH&vlxAXt+-I5_PBKMiI8{~@4{E`4$w7z@_KGVGsL0r}t$dAh)|4weF@3C$r}$d%4N z8k{yjAlq;0T_H!YoZ<`T#&WHkTdmw_QOoXNd956%tt{%5AXU_|FqBQ|79*cKR6IEp zbdC?b@f850r(;cS%V!0M8Ek1_1!-{0O{n$3wQD&DN(6l8@OxCTU#6i^;Vs_~&9Nja z2iMB@4Yjy1Xo##>j(J(k+u!fAydX=?S|0`k1{qdf5!1o;K|77~2D6n4s1$sPy>(cc zclxwg?pn|pmotmmWm&k)Emp41vRRG&*k)GVNs@X?Fp|wWNUGhiOd+Z_D+DLyT2?ei zDY9UEX+%tlf4OzBJS%tlw6*1svR}?vgZwC!6~sp>IxFZ0bTL4qkhUXS!YD#8t+*!% ziAudy(hZmcJ!7ogG615jI6_eoRfqvGBoo5n(1w~j3k;wwffMO(S;MUi`{T3q%Eanw zzy;K3_Ax>YK>(JPVhSls>KG@~5F*YUnJ|EbX9)i3CnY}7sAcBMDprUe>0*DHWr|+i zWR*&QAw(SIs4_2)MvXLR2HRC`PnigLa+iaVSvy4%6+nfTI1|6V!T54pU``|5Q6(0l z6_fO_<^y-N>?I9PFd3i~3eYQBr#2~vD`E^-aPdvUIxrXiXjnAvvO(M7Xs;0QgiaP} z%KR6Y!%=MGUHO5c$29WI?Z0*p_)E!A;i38CZed zRyIWQikV^9nH446ay87N7G~z==@<#p(b!E0O-ySWC=qP4ynjy*`>- zECH1gp;9iga)*O`8BkOXg-T3I5tzq4U_;bGZGZF-}zr2YhI6$}OSaVe2q#h|%DT>!a9 zqyZ$+3Pi^mOqwRQ7$+^AQ7wgVojMRMA*NJptoTTcR6i z{UR_%UTv{LZT3i)V=}=}3F?A#q3TF$Vn%3A%}3D6(~BjjFaxN_gyxY4@YwKZJnx)E zH3U65BS*OKHYwBTeF zuAOd9orT)Gp#+6y$DJxMg8h?d2CZTTC))d#GDp8@XjTZNiPE82y(DNwd(%;9ZW(as zwLXVFgh{gdNzmaL5a|CW-~{$Sb-D#@smA{f`aebkJ_udTSa$2iwUcR*$#~~P^Jujy z@tNHwxyWc973IOVEhlv%837Q4{683IctZ)dheU_*hD%G>32t4MJ^)^wT7HS(Q$UY+ zu3u6}$#3Pzj;aSz069I|K!Y#C?Wl8q^Z^_^%tK7VnW=0FQ(*hl>ZqPfD{p?a|VJ2E6 z`$FjLMbsA^VR7P;{rC*UX6A5GnVVR73xuN<2;bA&T1j=2D;-sIr zB}FPh4xOd-CvFs02&KbtlQc%M(6_Jzju`@*G_b*b35>wEU!E7+A4z&|3i;`T*(hLw zBbyL=H~-`E%(xo>C;Uc%Nue*GcK{zk3=9VEkgP8emUJ^rEW`nNqCDzzY^p;3jS4GI z76~)q$7kzy-3y00!3A)Oi;J_wnxL@cB1pV>~WHqtwpncOsTY==*%<@&)(Qdo+0dHAFOBfgf_v0c3P1JgKhp*4k%-WR1@TNvIJ}Ijv!_pgzcj z#^;x_(xpg<^pY;N(i|e@6qDnOk84&m2d*I_tq5OgYAhGV%6|POvQ`9GPQ2JRCpiwF znN6n5-sP_FVa^Z{xEx=h3cG4BGAgKguKt!vyKdV?xxx-jE{|o|PrjvB+QG^2Q!qgm z7V;1)H?PC*I{f|zvqM?gAp$!#u2u%_){=Svj@EEIv<;s@}fUH<1#QK?=qcyC<<^fClOy1;ibYq6_AY>cx!0Mb**~zgECU6!n-0 zPOpwBQB@$pE`zHGl~8~K{A3B$augG0r`N`dk1EqZbSww9Q3#Zs5={YonE;lwkhqdW zp<)*dq@a=@NnZKImCyD&Q^aU;aEeMM#4Z>>VHjqIQ0WBMFc3BYl%Q=vd|C{XTPX-! zGye|R-+=h1LNyR)AsHt{OF-@G5KwaIRB-i!{7wIpTy?!Z+LCdPWq$`}ed642%zl4WINIo-12 zh&>}&Q7__Ip#rfmp#* zB18CEXa~PvwhJKS5a9NfLgJ6Bu84C%SQKNGkPC_?K@GUobW2>NOo)l#X|RYhG`oE- ziGnjEFi2#83?Lsi$+1wlFBcP#3bhnSI;Ds-DvD#OKfI)t;GhCbswXCd{lx(2CjzC$ z&=u9x3TYIYM;J`8bEDZE8^Hr6R69O^&^Js#=b`&a;8qZFC%{C^(@%tSGnfB{2!5_5{(FsvM$q!AMZ&AGQ(GK|Kj(XG4g8k4uB$o)R(P0i*u7G#(TwDDMT&mxyv)swDDiwo>jR ziqaBnuV_OS29%^$UgQ$U;((yU7NCk|8Kg6OPLje$vhKtDaEqJ>7BC}RLW0${z!W}_ z-b^PeM)+1N1G$AHvPC=NoOTA}QW_WoUMcwTU>?y&EhinAU@*!bU>(lb5QVSj!s;G5 z&8+k+f<+=hGc8{^d73^<=eW?s&v<&qEP^bs`zq6c28H}zgd!_DGYAu+P;b<4)1#svvYS4^434a`Fk$BrZ=!a^AVAMIu|79-}_BU>0LT%soBk5qHVN750FxhrfR z+(mnM6P_Ec8jfk?=Qr1GP4c(v>!wpr= zff^%Y{W6eL{3j9Z?AZ&iM3xi~KRJcgDOt*F&=d5LirKu`M1(Bj{bHGj2A91Swembn zLWRx~C}2-T#EHs8 zUpE?3UT==#w~tuUccDynSMh~W3a-H-&A*80nK{IlPc0GfL`Y>BRBMhV10doI6iIf- zd`QmZJ4#|~B>%W%sB1L1ECh{Gczr$_!~cE#=n$2-%#nzPJs1ta@~u2RvY$?K$VL1> z?`Hgyk@>mrYvju@Umo^o>8OBYuY6e>;3OB#u{?iNpWJeiXt*f8%Ncolbd3R_lz=L8 z1fMyoOZG@2J2vm2i+w{eQgMbk6bBA~gOxAyH%DD2@0rV6mR-!o@|v>ttdg%P>nNX@ z%ik=!MV>PcKb?8q==0^%FY)I_56Vbv?Szmq(DxvT=he5Df<$By%EvetaIA!jFyb#-{R&s{gVQeiOcL=I>VJ=6<(? znYYArGUdqq+MC?v_=abIQz7N%%{(;Wyp&o{Rw5rmiMl{G^ANv!LR4PAhu<=xNB=1? zFKWD)9Qw#>_C*7@bpg9>h8G!hvo?zDMKbU-1RdglyKMh?QZ}dAyJo|$5ZBZBFB7^( z7Vjf&NC`^N89MxhfIWL3FPhk0KDd`poj5{&zAa=b&W}yZ)K<;M5rU=F(F^CjqB*%| z2DJh?iO>j?CAPOgs(=biN87V_TGd{O;QGKGF^5FlAY&odS9g*RALUD`bJ#n4V|BmG z+jncgJ*|~a#h73Q+Ieqbqmh47l-d?d%1g!I&a{es@D&Zw5R}M%K5CNXzw0*mab^{5 z)*pO@FPYS=O&y~Q8cbDpIRYSYMB*#ioB@wSAXAha{*p@gfk}nghXoylD}$+bC&5L$ zg{gCDR>`L}@`jomdG{XvaZMhGuynE|pW4S`liRV0{Nl+iT-){n7iJZoIysAIbl2o= z)cEzu7q@;|jY;>|%k5DJGx=yc{9Z34f;VjhCFi%$XUvMHmvqKIWk? zP-|xmKnyS3XL%@b4EYa|eQ>Y9D#Jtb5SNvE_!~Di3*ECD;$}{;KZjEYEo?8_!+*Om zDj(Rxb8c!iWQWs5qArSkN+M0p=$Kmoy!EZZV{Pe75g3}1Up3?w8_0v=po z*>mp42#5is{Hw?v$A&FXVouNW`5s|V-;R5rB;12WbP5+Tf%*2t%?mn4W%3RefK>Rh zUtPd3*KOZnDE|JGyq-ssSOrHS32c~)5Y^fXy!M_w)bLJfn2Gx+#&BBL^(X`H6;5qK zY;xVyZp1Xpre589Tax#LWsNHKv!_UE@7YWv(e-rFm8yNag^<9!6*zIO$?mnjxuw=X z&9`29^AOhNup`+dSB#NSDoHqMH#mh zcHQ-0J-bdo^EMCw^-0k1!rI>KRz9va6N+(OZT9F!Ehrb+i{^j{p$lutNLW+FXWEc9PexGexFgKxmYhZUiW(^^ zgD1g)=C}1LRx2I(qtkNa#e4X^X&3qy@4+5044Q-!g5l|xIrktP-A|a_#Y6SZ-TLY6 zsnh$@JIf7E@<2S>dk2fUEaLJ-ylZ^%`CoPc9mWZUCk~7QdJ4JB#f(zwW?#!qsW#$@ zB@}`V#Dnd?0`AdfDQ9(&j-1hvthE~fs=(GVmRT$zls~rJ-{ z;1W_GnfD^#wBJ3(^Jk7@_wl=C7U98>Lo>@6=f$)B&F<&-&+5q@;D=_l%vt(0h-YvUA%Ymz%3``V#Rkni*nH zW zk;R+?tKc*YC{6>Io0zR3X^Ua+=X>Y%X|ov^bmp>Ecnp%QVr+sQei%*|Q8I>lnwxot z`OVqA{L=XydZ7=N0DkETFA^8JSapBFP>^Sgy@i?USd0!iTF9%zwnE*)8Q;LS&2JHJ z!UvYVRz&V(w#==V>{7VcaKo`+5j;TDsH^z|G2f#fXyRbC)JB=dus4iORQET0c0>^o zAxhvqiE7_YnJF03!Q#w!eWr&FXH)efeAI&X(sr{d*ml16wgP*HVw2>$V0C~Lq`YVv2s{z$fMU5Y#)5esvz4zIFhDCvwSVglQcbWK zO=v#Bd*0rD(5vF~t6(8Hg_6?P`dcR_&YuCOXTe_GP(t>L?2}vzMvt32nSH?NNzRaz z;^!M~x7cd_`t5JAXZfmyX<5rz;jycb(6v=Q5Q4tMVUdvAo zq=3DJ4HF7q(eZy$qKJ-Yh{_ns`1m`lxQ})-J(RDc%siU9kbgpJ+SRjAi8F8LB97n$ zkj^GS_raO5|6r7trm>M27JDz^JOTMPIPFvuH3dY7nrXk+z~IhF6&linHnjAp2iYep z#e-8@z&Eo@8f2h3o98adV?KVtBDg5LWKrRjRxs)X2+7$!9VE&AUaAPFvMjQTD3EOv zIdsxT0-{7yG>rM|U5q5ciyR`9D~3Ye#XZpS@H2}B2eu2@R)jI}z0EIL+}(9@4$_oz zQ9TQ2dEF2i&>^~SSw$elXDA-LVg#6x4X-zV7YdPM0s@;cy%U2p(3wOiB>O)8@#5}r zoC@(F$daO1DkbKelr#<-!cx5RD1HC=xB@p2r>N=d+@jaXy;6`GK<08H3d!GE>J*{~ z=ebDSoC&tRgDdk|Xgtbp!(0+O(af8ElHs)vr+ssN=aOh>lcP?du@Vhd^1Vw6mbCR? z2ft)WCw}(M!K@1(eAh#IS2|YFl~1g%+B%}%&-l2zz47gA1$%(q&xQ^ed_k|m9gHbz zR?{I)GR+`*iG3&f*V^VxiMD;~X?a?|9=H`d_&Fk{BVVq?Z!d(G8XwP~L#K6zJM zHvE!7eL8pVE*EIJ%k47^e;{b4gu;>3jLhtuUCe6Uu4AW%*+XnKdyp+>_p&j~@WE>+ z($mxG+1;$=-Rv&ry_4O^mNCwjvL$R69`Cvh&wR~fGguvqPoGwM%gs}#R92La9ecy* zvQZ;PlwM!r`R{+O`}dXqy8P0ME|P`~8#ti48>WH*DlQ777m z+QvKF+dZ|QyK)1Bv%qq#}T=nsDGldIujF9g^BEw$oY^rFlC7UA+)#m)2>5eO5SRD*EU<| zjFn6n#6*_bg~^xsQHHbOyRo5d8e5X%@>$Ez_lcT(9Ah_4-2Iu5sd@Kzg_E%J z{%QZGkSy+(VYuSdX3_Iwyl0sI*zK|1rLMMa$t$wE)biRYWb?D}dLYHZkhL zWmn`HuuE&y#=xpw(mi>?Lo-0Rq3yS|jzB4$>D&P;V zynwCc$5-~~hfvXjvw7fs{|X@7d<};I@HJ6L&;uYjj0hYvQfwq)Nc;I8CXzEkMX^}^ z!~FVHJwjL~`6@EJLG01syO2M+Dl=YS70!wlkfV&N*|9 zo?P4@6-!p(G?Wxwm1Py;d|s>&g~++19A~MqOZBBR&?-QOiO3R*k&?x<`GN80IHkCn z7wt?fugOM5XHi-l$6y$Lj@21p5H}U@k_XdTJ_LZy)gI>SUBIIaM5)x`J@{$pHHFfBmmRT$j-LpD}%@9NCV zSu&1htP#C|X}1+I4}dXdG2}@i4_WO5IH(BqKBCO0o0=p)=5YB5T1&P z{ZxUi)v&RO227N`$^7>-V;OH)9lKD9f+tAL0n|EqBDu6M&|&+{yYR9-pUt9SRFD>= zqyJ%hKZ@Zi``}!FIgEN9eW*?5Je)-U2O_9P+l~`$nVcB~96gVZdZ;hk!`D6Z?~C@h z5V;VaHjJ7GNOopTtfmg|BOTT9rVGOpG!A>wOcn~@g=Jb{;Z`|7A%%D7E`G_wc`3`8 zL8>uKO9aTqeLVhfDMGSi5BFtn@|J6cvK4&dn)V3Q9$1snV+oofWkazebiW;`fcjsK zvL-^?viX(^PH~f5QwjfeO?Hpj8x?!uMntb*eJFzNgL*VIb4;LriQtxXm&kW*U_C4#8UQa^ zbQT8N*?2w#553&NqmQMws!gDw5d`p0V!C%QU4KL}J}mgU$1a4QxcspJz7r?Om?LsG z7xR;ko!<}2U1&!n9>p#L?L-m#*w4cGUMO+}Hgn<*8Dyk`M2bSJBu=X!ID>-ZW$RmB zDN)oxt}i9_v5!D~cA!7*b}(Z;mQC|G>|(y_f~sxg6N-Qf2rCBmRoBl1yDIah^CP@V?4!Wz4 z6gK7y0eE#o2+B{aLGb=O{`H0&xZ0k_A8a{?Sq360h&CNz!O;@4tedw(<)$wPu&| z(VGej<}jphEt&GP67A|ObJhq{w^8!pns-<7jd`fVVa_&+6)WI-HWgn)1;7>2j6BqQ zd02`>v1CCp_MEZp2rM}5X^XOCR(A!~9HX0CDF-KYf=_M#aaNhw8y7|iFL^Ufk^M7x z)#hj}TIfKE!@dv^O3miusk#3CxKYRWhRtnM$k+`2{^sl8Ds|t|za@Gg34#Pb0{M)P zzqkqciv{vmZE2}uci~pPZ%Zduz<=Fx8NP>YZKjI}XY-1!SxT)D=kvGXy)TTf*_xNN z--RS9g)BDC(2Y2?D$mM zo=?b8T`scn%`yUCl8bN?1L4>|IS-H3W3Litd5EE8JXyNrskSBjOHv|rz^(`hfFwHg zi0)V@3}u?RT!E^SKuP2n;8MK@rbMQ}e4jKR<>Q<}^8*qI9Hi82%J|h`*QdY#W>6yY z>5lBbeDKrPvG@6!r`xd)_*+l+2wX*s1W?@ICF4py;n!5&VOtP8cFvVM+91)fALAyW zYTTd#i4D1}l-J#&7&q}-wq>&$`Q6*Bcr_pe{}#kRTqquFmD(-J%>#Id(EJMNBcKEM z>!J8^T|%C(eeeU-{4%jl90Hm|5)aqmYh=@~B4&Pp;jnZWW#1&q#IS?RSI{dthchJ( zF7fWqM2#CMoA;SCz+Y|L%tt*FHIE3DU1Lm5=mCD`Gb7Sp-2|-SOe!89u*Eeffrqzw z;MrVv2_0Kv=04u_*`nsR{vz4GBl344lb8jydx}8|eeoxe>J;L7_(7Gt{@GTD{&zjw zHRBa14a|>osaXFBFagQ6Im5-h+jH4-yv_D(<9R((Y+^yFeaJ7}9!~qvm5HDZ&te%^ zwUTMezL<{5Z{D8GKIHY=qwVY76=5W46u?FK5ghyhAs|c$fSfX9lz=~D3O~NRm4AU6 z(?KBJ#_r%0p1va%xe~1eKLEo>JmIN5a}~d6$Hfr&h%I#my=Fyq%{PGPr(m;>MRP&2=PLLly#-H1{idFH_ zUEwf>I?y!K0UCDe9gBP4!t*ae4L(1&ID1Ykzx{{a($Q$gTRkZw#P zh;BeLOrJ#xfvNoPb1naiz~gUDAns*>1`&-U7Ie;PNFTv%US}QCM zf~L#-W0R$$jj{aNhOFLWfd>RNa2sIsVzzibL)e+cs#O3>wOBRKL=pptf+7Ocv8|gM zI;jYT$ME9~ec2eE|NOPh3e49815|=}>VN|qXNFtQI=3!={t*_$p%%$LR0kO+M!Jq) z@nU}DsLM}FM_clA{a9bLfG>WrEjz}yzu1L+!oPblqt~bK%%KV)N{A z%+Fm$8GmzkZ%prhsdZ-@r4YK2CV|w6VBOq-xtlyxbb=Q%io_gtbOay$QZ$Z5(E%Q^ z=1s{E8ha!#kS|{^RA?dr3^^8#$6@du0e&FyyEpL*ULu|(J|z@kKb;E5PIh8ld$WUd zhne!bk*UH(3)GRD<1ngbvdwRm7_-#*b_7(58 zw_Urh5ko?@eo-h|rx(Eh8v^;bMBbNn(ZralRmgAO+ZRtm9oU<5(Y|doJsujuq#!0N zIuyT)jzd$9g_ww$C$J1UMu9W+DbCsf{>miSPa&U)^BXh*@3pUImshb>N28d1v3VmL zC1*b)&m3)wJYJqT7H8Ci^Pq?jWi?;3uQgsD_xiqGnP(NW_FqqEMskJo4 z%f1KmF9WEAdjwN%`{Y&vd@%rD%>UfqHE$V6Oi;ZFjh2P|4qP6TMa=*3BRUp0{MBr{ z+HKscQ`j>8`Kv`fNA1_4{|a9CS_ab6Lte{gr}(7TdZj<(GX9+ii4s0*5s3CZ-~C$0 zjPH^768uU|(0R}nLLGU`kPSsHoAwH}!R7gZpL{(p{v*ah3qc3Us$gK^x@1p`O)kk;}{r%|M155sk^adhW#2iOm%BPDQbS5 zk8ggn8Dx!M!j!SL=1V;H&0P0s+J01ye*@L~!r@fh(eumI_jT2r2QKTf`bnfPl zzu5|w|D882_A)>7W)Z%-zZDy^X`=}6V?I1YNca*9`pISA^=^Vp*V5AXFc>ZW3zJquuf1f&;0p z7Ni|5nYVqrAWqCo3vGvAD79F7^JiMu)xygbs1qtms79-u)gDCsf*SnWAvsl2z*S67 zsIXbZ>{4vi-UfFKU-tTkb^C$eC7k;KTSK3y<}U(gaJWb(XBgdleB0aY@G8LLZ}$M6 zat@9RA|tupd<=TBJmy*tqqXQ@-sPQ#gGcb3NTJoa_HwcRMy97A))|HW0sx(9uO7x9 z`C*QcU$PcQ)*h~-@Timy?~I# zpd<{>dt2khhqBlo{J)2~Vi%Ynn$P~^IfvuEzeIC9>0|!N*BMum|v|NV;<4y%tbqh3_?!>)d?Ud;M^;1UEC>eEoaFexV_j7kr?m zLDJw9ks6;e)ZYd2N+#zee*TAf$kYAj!!{YWx?wGn9uT;kfQEcN%pdr0hFquc=tnJ6 z&?Zp>A4oB0DEz9AE=r%{_MdMQkx`)B0{NRa;JScs|EOzN!GT$%D@hEQ{X5yWOwK-z zkQU^tKF#C>ALm{3n9^P8L6F@pN6C0%zZpGz{qf)i9$>+7957|N?LnZSPQ>$41bG{D z5WYyW_QTn-D(E2%;B!TBoC&(w=Kai<^tI6m$?Q zU@SFX?uWIJw-F1Nf66eb0X(Tw-)WLNBOB!#%=Iv7;?*{2KY^$TzDFT{;Al&2&Ri%Z zzW3;jDFxGLE4?e722d+D%m5ZHIaVB8LG`3BLCBDKfUi6@1z2zPiH&!RUi-;Fb~j)B z$q?6r*gFY9*Pi}lu-~y5FfMm7Ba^@OQ6?YtX$X>Z)2C%FdUzb`=AV9gHA?y%uYf$Q zJKo0w_c>oa&9J(Y$D6@Ux;|@14Au3ssP{1?5kz2j;j<`u&G@We{B@U2FFO!kX+iSP z`PsA5rGDGO?mg{z;|lysp+v0k8mt*^E1gv@q<{n}!@>ok*l|4>Sc(t8E`tIs>aWs&WCI^Jw`uwRtSH+cRR&9UrL@20hl%8u-i zo4!eeRbs+T0B8U2guob;XI%j7+HRn9!$_cC06v&@3MvJSMx+5kgt?RVEow z+3E-bI^wG$yexP1R~4Rl*!7?-tOB0?b)PF|yP;ru(!-?S2W*#`^Ptb1WEsPpY$6

U>6?B55D4N0lKg1WE?7&{*J5Mf_ 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%0A08yS;{zlPS636dJn6hVIeA?8@B+la(>qQj9dN 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

hO`G=@{4w}>{*!-dB$i+Fp4iuH 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 RNGOSKrBc0IQAV}L_rL4l6Vm_y delta 288251 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

}o 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

j;u?`5cE`DI0rMU4mo{qr4bN@n_l*FcbE< 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 E2k{{)GXMYp From 92d616d2460dcc6e9985ad265ab3ba53e5f73ef2 Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Thu, 9 Apr 2026 17:59:24 +0200 Subject: [PATCH 3/3] fix: replaced http-server dependency --- package-lock.json | 147 ++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6640f21..169a3ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sqlite.org/sqlite-wasm", - "version": "3.51.2-build8", + "version": "3.53.0-build1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sqlite.org/sqlite-wasm", - "version": "3.51.2-build8", + "version": "3.53.0-build1", "license": "Apache-2.0", "devDependencies": { "@types/node": "^25.5.2", @@ -14,7 +14,7 @@ "@vitest/browser": "^4.1.4", "@vitest/browser-playwright": "^4.1.4", "happy-dom": "20.8.9", - "http-server": "github:vapier/http-server", + "http-server": "^14.1.1", "lefthook": "2.1.5", "playwright": "^1.59.1", "prettier": "^3.8.1", @@ -911,6 +911,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -960,19 +961,18 @@ } }, "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } + "license": "MIT" }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.1.2" }, @@ -1053,6 +1053,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1080,6 +1081,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1091,7 +1093,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/comment-parser": { "version": "1.4.1", @@ -1115,17 +1118,27 @@ "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decode-named-character-reference": { @@ -1296,7 +1309,8 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/expect-type": { "version": "1.3.0", @@ -1327,9 +1341,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -1337,6 +1351,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -1459,6 +1474,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1494,6 +1510,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } @@ -1510,6 +1527,7 @@ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, + "license": "MIT", "dependencies": { "whatwg-encoding": "^2.0.0" }, @@ -1522,6 +1540,7 @@ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, + "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -1532,8 +1551,9 @@ } }, "node_modules/http-server": { - "version": "14.1.0", - "resolved": "git+ssh://git@github.com/vapier/http-server.git#35fad5cd29005748916d1bca24db83ab6976ba41", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, "license": "MIT", "dependencies": { @@ -1563,6 +1583,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -2032,13 +2053,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, - "license": "MIT" - }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -2561,29 +2575,12 @@ ], "license": "MIT" }, - "node_modules/micromark/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -2596,22 +2593,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -2685,6 +2671,7 @@ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true, + "license": "(WTFPL OR MIT)", "bin": { "opener": "bin/opener-bin.js" } @@ -2766,17 +2753,17 @@ } }, "node_modules/portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", + "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", "dev": true, + "license": "MIT", "dependencies": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" + "async": "^3.2.6", + "debug": "^4.3.6" }, "engines": { - "node": ">= 0.12.0" + "node": ">= 10.12" } }, "node_modules/postcss": { @@ -2865,9 +2852,9 @@ } }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2901,7 +2888,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", @@ -3009,19 +2997,22 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "7.7.4", @@ -3057,14 +3048,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -3163,6 +3154,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3400,7 +3392,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/vite": { "version": "8.0.8", @@ -3915,7 +3908,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", "dev": true, + "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" }, diff --git a/package.json b/package.json index f328f2c..7fa58b5 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@vitest/browser": "^4.1.4", "@vitest/browser-playwright": "^4.1.4", "happy-dom": "20.8.9", - "http-server": "github:vapier/http-server", + "http-server": "^14.1.1", "lefthook": "2.1.5", "playwright": "^1.59.1", "prettier": "^3.8.1",