diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index ffcc691..f053392 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -8,7 +8,7 @@ const path = require('path'); // this process by nodejs-mobile-cordova). The copy under node_modules/ is // whichever architecture it happened to be built for last; replace it with // the prebuild matching this device's actual arch before anything requires -// better-sqlite3. See scripts/build-better-sqlite3-arm.sh. +// better-sqlite3. See scripts/build-better-sqlite3-node18.sh. (function installBetterSqlite3Prebuild() { const archDirs = { arm64: 'arm64-v8a', @@ -31,60 +31,14 @@ const path = require('path'); } })(); -// Node 12.19 predates crypto.randomUUID (added in Node 14.17), used by -// services/authService.js for session token IDs. -const nodeCrypto = require('crypto'); -if (typeof nodeCrypto.randomUUID !== 'function') { - nodeCrypto.randomUUID = function () { - const b = nodeCrypto.randomBytes(16); - b[6] = (b[6] & 0x0f) | 0x40; - b[8] = (b[8] & 0x3f) | 0x80; - const hex = b.toString('hex'); - return [ - hex.slice(0, 8), - hex.slice(8, 12), - hex.slice(12, 16), - hex.slice(16, 20), - hex.slice(20), - ].join('-'); - }; -} - -// Node 12.19 predates crypto.hkdfSync (added in Node 15.0), used by -// services/encryptionService.js's deriveKey to derive the token-encryption -// key from TOKEN_ENCRYPTION_KEY. Implements HKDF-Extract/Expand (RFC 5869) -// via createHmac. -if (typeof nodeCrypto.hkdfSync !== 'function') { - nodeCrypto.hkdfSync = function (digest, ikm, salt, info, keylen) { - const hashLen = nodeCrypto.createHash(digest).digest().length; - const saltBuf = salt && salt.length ? Buffer.from(salt) : Buffer.alloc(hashLen, 0); - const ikmBuf = Buffer.isBuffer(ikm) ? ikm : Buffer.from(ikm); - const infoBuf = Buffer.isBuffer(info) ? info : Buffer.from(info || ''); - - const prk = nodeCrypto.createHmac(digest, saltBuf).update(ikmBuf).digest(); - - let t = Buffer.alloc(0); - let okm = Buffer.alloc(0); - let counter = 1; - while (okm.length < keylen) { - t = nodeCrypto - .createHmac(digest, prk) - .update(t) - .update(infoBuf) - .update(Buffer.from([counter])) - .digest(); - okm = Buffer.concat([okm, t]); - counter++; - } - return okm.slice(0, keylen); - }; -} - -// nodejs-mobile-cordova 0.4.3 embeds Node 12.19 built without ICU, so -// `Intl` is entirely undefined. Polyfill just the bits the server uses -// (utils/money.js, routes/dataSources.js, routes/status.js) — en-US number +// nodejs-mobile's Node 18.20.4 build ships WITHOUT ICU (verified: +// process.versions.icu === undefined, typeof Intl === 'undefined'), so `Intl` +// is entirely absent. Polyfill just the bits the server uses — en-US number // formatting with thousands separators, and a no-op DateTimeFormat whose // resolvedOptions().timeZone the callers already treat as optional. +// +// (crypto.randomUUID and crypto.hkdfSync are NATIVE on Node 18 — the Node-12 +// shims for them are gone. See PLAN/FUTURE Batch 6.) if (typeof global.Intl === 'undefined') { global.Intl = { NumberFormat: function (_locale, options) { @@ -114,6 +68,10 @@ if (typeof global.Intl === 'undefined') { // on-device — nodejs-mobile-cordova copies nodejs-project here at runtime). process.env.DB_PATH = path.join(__dirname, 'data', 'bills.db'); process.env.BACKUP_PATH = path.join(__dirname, 'backups'); +// Android has no /tmp; point os.tmpdir() at app-writable storage so the daily +// worker's temp-export cleanup doesn't error (ENOENT scandir '/tmp'). +process.env.TMPDIR = path.join(__dirname, 'tmp'); +try { fs.mkdirSync(process.env.TMPDIR, { recursive: true }); } catch (_) { /* best effort */ } process.env.PORT = process.env.PORT || '3000'; process.env.BIND_HOST = '127.0.0.1'; // The Capacitor WebView's origin (androidScheme: 'https') is cross-origin @@ -123,13 +81,30 @@ process.env.CORS_ORIGIN = 'https://localhost'; const cordova = require('cordova-bridge'); -// Wait for the WebView to hand over the device-bound encryption key (stored -// in Android Keystore / iOS Keychain — see src/crypto.ts) before starting the -// server, so encryptionService.js picks up TOKEN_ENCRYPTION_KEY on first use. +// Surface fatal errors to logcat and to the WebView (LoadingScreen) instead of +// dying silently behind an endless "Starting…" spinner. +function reportError(where, err) { + const msg = err && err.stack ? err.stack : String(err); + console.error('[main.js] ' + where + ':', msg); + try { + cordova.channel.post('message', { type: 'serverError', message: String((err && err.message) || err) }); + } catch (_) { /* channel may not be ready */ } +} + +process.on('uncaughtException', (err) => reportError('uncaughtException', err)); +process.on('unhandledRejection', (err) => reportError('unhandledRejection', err)); + +// Wait for the WebView to hand over the device-bound encryption key (stored in +// Android Keystore / iOS Keychain — see src/crypto.ts) before starting the +// server, so encryptionService picks up TOKEN_ENCRYPTION_KEY on first use. cordova.channel.on('message', function (msg) { if (msg && msg.type === 'encryptionKey' && typeof msg.key === 'string') { process.env.TOKEN_ENCRYPTION_KEY = msg.key; - require('./server/server.js'); + try { + require('./server/server.cts'); + } catch (err) { + reportError('server failed to start', err); + } } }); diff --git a/nodejs-assets/nodejs-project/package-lock.json b/nodejs-assets/nodejs-project/package-lock.json index 73e8596..edc386e 100644 --- a/nodejs-assets/nodejs-project/package-lock.json +++ b/nodejs-assets/nodejs-project/package-lock.json @@ -9,18 +9,18 @@ "version": "1.0.0", "dependencies": { "@simplewebauthn/server": "^13.0.0", - "bcryptjs": "^2.4.3", + "bcryptjs": "^3.0.3", "better-sqlite3": "^12.9.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", - "express": "^4.18.2", + "express": "^5.2.1", "express-rate-limit": "^8.4.1", + "kysely": "^0.29.3", "node-cron": "^4.2.1", - "nodemailer": "^8.0.9", - "openid-client": "^5.7.1", + "nodemailer": "^9.0.3", "otplib": "^13.4.1", "qrcode": "^1.5.4", - "xlsx": "^0.18.5" + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" }, "devDependencies": { "nodejs-mobile-gyp": "^0.4.0" @@ -464,25 +464,25 @@ } }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/adler-32": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", - "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", - "license": "Apache-2.0", + "node_modules/accepts/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { - "node": ">=0.8" + "node": ">= 0.6" } }, "node_modules/agent-base": { @@ -533,12 +533,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, "node_modules/asn1js": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", @@ -581,10 +575,13 @@ "license": "MIT" }, "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", - "license": "MIT" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } }, "node_modules/better-sqlite3": { "version": "12.10.1", @@ -621,27 +618,40 @@ } }, "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.15.1", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { @@ -756,19 +766,6 @@ "node": ">=6" } }, - "node_modules/cfb": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", - "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", - "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "crc-32": "~1.2.0" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -796,15 +793,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/codepage": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", - "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -824,15 +812,16 @@ "license": "MIT" }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -889,18 +878,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -940,12 +917,20 @@ } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -990,16 +975,6 @@ "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1170,45 +1145,42 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.15.1", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 18" }, "funding": { "type": "opencollective", @@ -1233,6 +1205,15 @@ "express": ">= 4.11" } }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1240,21 +1221,24 @@ "license": "MIT" }, "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/find-up": { @@ -1296,22 +1280,13 @@ "node": ">= 0.6" } }, - "node_modules/frac": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", - "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/fs-constants": { @@ -1500,31 +1475,6 @@ "node": ">= 14" } }, - "node_modules/http-proxy-agent/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/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -1539,41 +1489,20 @@ "node": ">= 14" } }, - "node_modules/https-proxy-agent/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/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ieee754": { @@ -1662,6 +1591,12 @@ "dev": true, "license": "MIT" }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", @@ -1688,13 +1623,13 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "node_modules/kysely": { + "version": "0.29.3", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.29.3.tgz", + "integrity": "sha512-VHtBdW6XB/pgoTSqraM3UAa2rYoYdNXqnNPpX+8XXP+cwYbVEFuAp3HyPt1vpNfU9l7Y2kpUrA9QDPsy8uUqOQ==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" + "engines": { + "node": ">=22.0.0" } }, "node_modules/locate-path": { @@ -1709,18 +1644,6 @@ "node": ">=8" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-fetch-happen": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", @@ -1765,63 +1688,49 @@ } }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-response": { @@ -2027,9 +1936,9 @@ "license": "MIT" }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/napi-build-utils": { @@ -2042,6 +1951,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -2094,9 +2004,9 @@ } }, "node_modules/nodemailer": { - "version": "8.0.11", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.11.tgz", - "integrity": "sha512-nrO/pDAUKl+wXX+lx16tDLbnm0fW6sK/x8mgohaCpg+CdCEl482bD4tCuAZk2DyliruiNTIZxRCoWkDqJEnAiA==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.3.tgz", + "integrity": "sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -2127,15 +2037,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2148,15 +2049,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/oidc-token-hash": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", - "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -2178,21 +2070,6 @@ "wrappy": "1" } }, - "node_modules/openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", - "license": "MIT", - "dependencies": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/otplib": { "version": "13.4.1", "resolved": "https://registry.npmjs.org/otplib/-/otplib-13.4.1.tgz", @@ -2319,10 +2196,14 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/pngjs": { "version": "5.0.0", @@ -2443,12 +2324,13 @@ } }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -2458,27 +2340,31 @@ } }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", + "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/rc": { @@ -2541,6 +2427,22 @@ "node": ">= 4" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2580,48 +2482,48 @@ } }, "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/set-blocking": { @@ -2830,43 +2732,6 @@ "node": ">= 14" } }, - "node_modules/socks-proxy-agent/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/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ssf": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", - "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", - "license": "Apache-2.0", - "dependencies": { - "frac": "~1.1.2" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/ssri": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", @@ -3102,16 +2967,34 @@ } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/unique-filename": { @@ -3155,15 +3038,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3195,24 +3069,6 @@ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "license": "ISC" }, - "node_modules/wmf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", - "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/word": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", - "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -3253,19 +3109,10 @@ "license": "ISC" }, "node_modules/xlsx": { - "version": "0.18.5", - "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", - "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "version": "0.20.3", + "resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", + "integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==", "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "cfb": "~1.2.1", - "codepage": "~1.15.0", - "crc-32": "~1.2.1", - "ssf": "~0.11.2", - "wmf": "~1.0.1", - "word": "~0.3.0" - }, "bin": { "xlsx": "bin/xlsx.njs" }, @@ -3283,6 +3130,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC" }, "node_modules/yargs": { diff --git a/nodejs-assets/nodejs-project/package.json b/nodejs-assets/nodejs-project/package.json index 40db0ab..8b11590 100644 --- a/nodejs-assets/nodejs-project/package.json +++ b/nodejs-assets/nodejs-project/package.json @@ -2,20 +2,21 @@ "name": "nodejs-project", "version": "1.0.0", "private": true, + "//": "Server-side runtime deps only (client deps ship prebuilt in dist/). Versions mirror ../../../bill-tracker. openid-client is intentionally omitted — it is ESM-only and unused in single-user local mode; scripts/prepare-local-mode-deps.js installs a CJS stub. kysely is ESM-only and gets bundled to CJS by the same script.", "dependencies": { "@simplewebauthn/server": "^13.0.0", - "bcryptjs": "^2.4.3", + "bcryptjs": "^3.0.3", "better-sqlite3": "^12.9.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", - "express": "^4.18.2", + "express": "^5.2.1", "express-rate-limit": "^8.4.1", + "kysely": "^0.29.3", "node-cron": "^4.2.1", - "nodemailer": "^8.0.9", - "openid-client": "^5.7.1", + "nodemailer": "^9.0.3", "otplib": "^13.4.1", "qrcode": "^1.5.4", - "xlsx": "^0.18.5" + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" }, "devDependencies": { "nodejs-mobile-gyp": "^0.4.0" diff --git a/nodejs-assets/nodejs-project/prebuilds/arm64-v8a/better_sqlite3.node b/nodejs-assets/nodejs-project/prebuilds/arm64-v8a/better_sqlite3.node index b4bb34a..523f545 100755 Binary files a/nodejs-assets/nodejs-project/prebuilds/arm64-v8a/better_sqlite3.node and b/nodejs-assets/nodejs-project/prebuilds/arm64-v8a/better_sqlite3.node differ diff --git a/nodejs-assets/nodejs-project/prebuilds/armeabi-v7a/better_sqlite3.node b/nodejs-assets/nodejs-project/prebuilds/armeabi-v7a/better_sqlite3.node index 015367f..f59952d 100755 Binary files a/nodejs-assets/nodejs-project/prebuilds/armeabi-v7a/better_sqlite3.node and b/nodejs-assets/nodejs-project/prebuilds/armeabi-v7a/better_sqlite3.node differ diff --git a/nodejs-assets/nodejs-project/prebuilds/x86_64/better_sqlite3.node b/nodejs-assets/nodejs-project/prebuilds/x86_64/better_sqlite3.node index ea4404c..fa16cbd 100755 Binary files a/nodejs-assets/nodejs-project/prebuilds/x86_64/better_sqlite3.node and b/nodejs-assets/nodejs-project/prebuilds/x86_64/better_sqlite3.node differ diff --git a/scripts/build-better-sqlite3-arm.sh b/scripts/build-better-sqlite3-arm.sh deleted file mode 100755 index 670d73d..0000000 --- a/scripts/build-better-sqlite3-arm.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash -# Cross-compiles better-sqlite3 for arm64-v8a and armeabi-v7a (real Android -# devices) and writes the resulting better_sqlite3.node binaries into -# nodejs-assets/nodejs-project/prebuilds//. main.js installs the one -# matching the device's actual process.arch at runtime — see main.js's -# installBetterSqlite3Prebuild(). -# -# The existing x86_64 build (emulator) in -# nodejs-assets/nodejs-project/node_modules/better-sqlite3/build/Release/ -# already covers nodejs-assets/nodejs-project/prebuilds/x86_64/ via -# fix-better-sqlite3-android.sh; re-copy it manually if that build changes: -# cp nodejs-assets/nodejs-project/node_modules/better-sqlite3/build/Release/better_sqlite3.node \ -# nodejs-assets/nodejs-project/prebuilds/x86_64/ -# -# Requires: Android NDK (ANDROID_HOME or ANDROID_NDK_HOME set), and -# `npm install` already run in nodejs-assets/nodejs-project (so -# better-sqlite3's source + nodejs-mobile-gyp are present). -# -# Usage: ./scripts/build-better-sqlite3-arm.sh - -set -euo pipefail - -MOBILE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -PKG_SRC="$MOBILE_DIR/nodejs-assets/nodejs-project/node_modules/better-sqlite3" -PREBUILDS_DIR="$MOBILE_DIR/nodejs-assets/nodejs-project/prebuilds" -GYP="$MOBILE_DIR/nodejs-assets/nodejs-project/node_modules/.bin/nodejs-mobile-gyp" -NODEDIR="$MOBILE_DIR/node_modules/nodejs-mobile-cordova/libs/android/libnode" - -NDK="${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}}" -if [[ -z "$NDK" ]]; then - SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" - [[ -n "$SDK" ]] && NDK="$(ls -d "$SDK"/ndk/*/ 2>/dev/null | sort -V | tail -1)" -fi -NDK="${NDK%/}" - -if [[ ! -d "$PKG_SRC" ]]; then - echo "Error: $PKG_SRC not found. Run npm install in nodejs-assets/nodejs-project first." >&2 - exit 1 -fi -if [[ -z "$NDK" || ! -d "$NDK" ]]; then - echo "Error: Android NDK not found. Set ANDROID_NDK_HOME (or ANDROID_HOME with an ndk/ subdir)." >&2 - exit 1 -fi -if [[ ! -x "$GYP" ]]; then - echo "Error: $GYP not found. Run npm install in nodejs-assets/nodejs-project first." >&2 - exit 1 -fi - -TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64" -API=24 # matches android/variables.gradle minSdkVersion - -# Each ABI's libnode.so ships gzipped; better-sqlite3's binding.gyp (via -# nodejs-mobile's common.gypi) needs the real .so to add it as a NEEDED entry. -for abi in arm64-v8a armeabi-v7a; do - LIBNODE_DIR="$NODEDIR/bin/$abi" - if [[ ! -f "$LIBNODE_DIR/libnode.so" && -f "$LIBNODE_DIR/libnode.so.gz" ]]; then - gunzip -k "$LIBNODE_DIR/libnode.so.gz" - fi -done - -build_one() { - local abi="$1" gyp_arch="$2" cc="$3" cxx="$4" - local build_dir="$MOBILE_DIR/build-output/better-sqlite3-$abi" - - echo "── Building better-sqlite3 for $abi ($gyp_arch) ──" - rm -rf "$build_dir" - mkdir -p "$build_dir" - rsync -a --exclude=build --exclude=prebuilds "$PKG_SRC/" "$build_dir/" - - # v8::Object::CreationContext() was removed in newer V8; nodejs-mobile's - # bundled V8 (Node 12.19) only has the old non-checked accessor. Same patch - # as fix-better-sqlite3-android.sh. - sed -i 's/obj->GetCreationContext().ToLocalChecked()/obj->CreationContext()/' \ - "$build_dir/src/util/binder.cpp" - - ( - cd "$build_dir" - CC="$TOOLCHAIN/bin/$cc" \ - CXX="$TOOLCHAIN/bin/$cxx" \ - AR="$TOOLCHAIN/bin/llvm-ar" \ - GYP_DEFINES="OS=android" \ - "$GYP" configure --target=12.19.0 --arch="$gyp_arch" --nodedir="$NODEDIR" - - CC="$TOOLCHAIN/bin/$cc" \ - CXX="$TOOLCHAIN/bin/$cxx" \ - AR="$TOOLCHAIN/bin/llvm-ar" \ - "$GYP" build - ) - - mkdir -p "$PREBUILDS_DIR/$abi" - cp "$build_dir/build/Release/better_sqlite3.node" "$PREBUILDS_DIR/$abi/better_sqlite3.node" - echo "Wrote $PREBUILDS_DIR/$abi/better_sqlite3.node" -} - -build_one arm64-v8a arm64 aarch64-linux-android${API}-clang aarch64-linux-android${API}-clang++ -build_one armeabi-v7a arm armv7a-linux-androideabi${API}-clang armv7a-linux-androideabi${API}-clang++ - -echo "Done. Verify with: file $PREBUILDS_DIR/*/better_sqlite3.node" diff --git a/scripts/build-better-sqlite3-node18.sh b/scripts/build-better-sqlite3-node18.sh new file mode 100755 index 0000000..2057bf9 --- /dev/null +++ b/scripts/build-better-sqlite3-node18.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# Cross-compiles better-sqlite3 for the embedded Node 18.20.4 (nodejs-mobile) ABI +# for all three Android ABIs and writes the resulting better_sqlite3.node binaries +# into nodejs-assets/nodejs-project/prebuilds//. main.js installs the one +# matching the device's actual process.arch at runtime — see +# installBetterSqlite3Prebuild(). +# +# Unlike the old Node-12 build, NO binder.cpp patch is needed: Node 18's V8 10.2 +# still exposes the (deprecated) v8::Object::CreationContext() that better-sqlite3 +# 12.x uses, so it compiles as-is (deprecation warning only). +# +# Requires: Android NDK (ANDROID_HOME or ANDROID_NDK_HOME set), the Node 18 runtime +# staged into the plugin (scripts/install-node18-runtime.sh), and `npm install` +# already run in nodejs-assets/nodejs-project. +# +# Usage: ./scripts/build-better-sqlite3-node18.sh [abi ...] (default: all three) + +set -euo pipefail + +NODE_TARGET="18.20.4" +API=24 # matches android/variables.gradle minSdkVersion + +MOBILE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PKG_SRC="$MOBILE_DIR/nodejs-assets/nodejs-project/node_modules/better-sqlite3" +PREBUILDS_DIR="$MOBILE_DIR/nodejs-assets/nodejs-project/prebuilds" +GYP="$MOBILE_DIR/nodejs-assets/nodejs-project/node_modules/.bin/nodejs-mobile-gyp" +NODEDIR="$MOBILE_DIR/node_modules/nodejs-mobile-cordova/libs/android/libnode" + +NDK="${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}}" +if [[ -z "$NDK" ]]; then + SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" + [[ -n "$SDK" ]] && NDK="$(ls -d "$SDK"/ndk/*/ 2>/dev/null | sort -V | tail -1)" +fi +NDK="${NDK%/}" + +[[ -d "$PKG_SRC" ]] || { echo "Error: $PKG_SRC not found. Run npm install in nodejs-project first." >&2; exit 1; } +[[ -n "$NDK" && -d "$NDK" ]] || { echo "Error: Android NDK not found (set ANDROID_NDK_HOME or ANDROID_HOME)." >&2; exit 1; } +[[ -x "$GYP" ]] || { echo "Error: $GYP not found. Run npm install in nodejs-project first." >&2; exit 1; } +grep -q "18" "$NODEDIR/include/node/node_version.h" || { echo "Error: $NODEDIR is not the Node 18 runtime — run scripts/install-node18-runtime.sh first." >&2; exit 1; } + +TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64" + +abi_arch() { case "$1" in arm64-v8a) echo arm64;; armeabi-v7a) echo arm;; x86_64) echo x64;; *) echo "" ;; esac; } +abi_cc() { case "$1" in + arm64-v8a) echo "aarch64-linux-android${API}-clang" ;; + armeabi-v7a) echo "armv7a-linux-androideabi${API}-clang" ;; + x86_64) echo "x86_64-linux-android${API}-clang" ;; + esac; } + +build_one() { + local abi="$1" + local gyp_arch cc cxx + gyp_arch="$(abi_arch "$abi")"; cc="$(abi_cc "$abi")"; cxx="${cc}++" + [[ -n "$gyp_arch" ]] || { echo "Unknown ABI: $abi" >&2; exit 1; } + + # better-sqlite3's binding.gyp (via nodejs-mobile's common.gypi) needs the real + # libnode.so (not the .gz) to add it as a NEEDED entry at link time. + local libnode_dir="$NODEDIR/bin/$abi" + if [[ ! -f "$libnode_dir/libnode.so" && -f "$libnode_dir/libnode.so.gz" ]]; then + gunzip -k "$libnode_dir/libnode.so.gz" + fi + + local build_dir="$MOBILE_DIR/build-output/better-sqlite3-$abi" + echo "── Building better-sqlite3 for $abi ($gyp_arch) against Node $NODE_TARGET ──" + rm -rf "$build_dir"; mkdir -p "$build_dir" + rsync -a --exclude=build --exclude=prebuilds "$PKG_SRC/" "$build_dir/" + + ( + cd "$build_dir" + CC="$TOOLCHAIN/bin/$cc" CXX="$TOOLCHAIN/bin/$cxx" AR="$TOOLCHAIN/bin/llvm-ar" \ + GYP_DEFINES="OS=android" "$GYP" configure \ + --target="$NODE_TARGET" --arch="$gyp_arch" --nodedir="$NODEDIR" + + # nodejs-mobile's common.gypi only adds libnode.so as a NEEDED entry when + # target_arch=="x86_64", but gyp reports "x64" — so the rule never fires and + # the addon can't resolve V8/Node symbols (e.g. v8::Isolate::GetCurrent) at + # dlopen time on Android. Inject the ABI's libnode.so into the link so its + # SONAME (libnode.so) is recorded as NEEDED. (patchelf isn't required.) + local mk="build/better_sqlite3.target.mk" + if [[ -f "$mk" ]] && ! grep -q 'libnode\.so' "$mk"; then + sed -i "s#^\([[:space:]]*\)-llog\$#\1-llog $libnode_dir/libnode.so#" "$mk" + grep -q 'libnode\.so' "$mk" || { echo "Error: failed to inject libnode.so into $mk" >&2; exit 1; } + fi + + CC="$TOOLCHAIN/bin/$cc" CXX="$TOOLCHAIN/bin/$cxx" AR="$TOOLCHAIN/bin/llvm-ar" \ + make -C build BUILDTYPE=Release + ) + + mkdir -p "$PREBUILDS_DIR/$abi" + cp "$build_dir/build/Release/better_sqlite3.node" "$PREBUILDS_DIR/$abi/better_sqlite3.node" + echo "Wrote $PREBUILDS_DIR/$abi/better_sqlite3.node" +} + +ABIS=("$@") +[[ ${#ABIS[@]} -eq 0 ]] && ABIS=(x86_64 arm64-v8a armeabi-v7a) +for abi in "${ABIS[@]}"; do build_one "$abi"; done + +echo "Done. Verify with: file $PREBUILDS_DIR/*/better_sqlite3.node" diff --git a/scripts/fix-better-sqlite3-android.sh b/scripts/fix-better-sqlite3-android.sh deleted file mode 100755 index 2d184fa..0000000 --- a/scripts/fix-better-sqlite3-android.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -# Re-applies the two patches better-sqlite3 needs to load correctly under -# nodejs-mobile (Node 12.19 / Android x86_64 emulator). Required after any -# fresh `npm install` in nodejs-assets/nodejs-project, since both patches -# target files inside node_modules. -# -# Usage: ./scripts/fix-better-sqlite3-android.sh -# Run from the mobile project root, after `npm install` has already built -# better-sqlite3 once via nodejs-mobile-gyp (so build/better_sqlite3.target.mk -# and the Release/ dir already exist). - -set -euo pipefail - -MOBILE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -PKG_DIR="$MOBILE_DIR/nodejs-assets/nodejs-project/node_modules/better-sqlite3" -LIBNODE="$MOBILE_DIR/android/app/libs/cdvnodejsmobile/libnode/bin/x86_64/libnode.so" - -if [[ ! -d "$PKG_DIR" ]]; then - echo "Error: $PKG_DIR not found. Run npm install in nodejs-assets/nodejs-project first." >&2 - exit 1 -fi - -# 1. v8::Object::CreationContext() was removed in newer V8; nodejs-mobile's -# bundled V8 (Node 12.19) only has the old non-checked accessor. -BINDER="$PKG_DIR/src/util/binder.cpp" -if grep -q 'GetCreationContext().ToLocalChecked()' "$BINDER"; then - sed -i 's/obj->GetCreationContext().ToLocalChecked()/obj->CreationContext()/' "$BINDER" - echo "Patched $BINDER" -else - echo "$BINDER already uses CreationContext() (no patch needed)" -fi - -# 2. nodejs-mobile-cordova's common.gypi only adds the libnode.so NEEDED entry -# when target_arch=="x86_64", but gyp reports "x64" for 64-bit x86, so the -# rule never fires. Add it to LIBS directly so the addon can resolve V8/Node -# symbols (e.g. v8::Isolate::GetCurrent) at dlopen time on Android. -TARGET_MK="$PKG_DIR/build/better_sqlite3.target.mk" -if [[ ! -f "$TARGET_MK" ]]; then - echo "Error: $TARGET_MK not found. Run npm install to build better-sqlite3 first." >&2 - exit 1 -fi - -if ! grep -q "libnode.so" "$TARGET_MK"; then - sed -i "/^\t-llog \\\\\$/a\\\\\t$LIBNODE" "$TARGET_MK" - echo "Patched $TARGET_MK" -else - echo "$TARGET_MK already references libnode.so (no patch needed)" -fi - -# Rebuild and relink with the patched sources/Makefile. -cd "$PKG_DIR/build" -rm -f Release/better_sqlite3.node Release/obj.target/better_sqlite3.node -make BUILDTYPE=Release better_sqlite3.node - -# Keep the x86_64 prebuild (emulator) in sync — main.js picks the right one -# for the device's process.arch at runtime. arm64-v8a/armeabi-v7a prebuilds -# are produced separately by build-better-sqlite3-arm.sh. -PREBUILD_DIR="$MOBILE_DIR/nodejs-assets/nodejs-project/prebuilds/x86_64" -mkdir -p "$PREBUILD_DIR" -cp "$PKG_DIR/build/Release/better_sqlite3.node" "$PREBUILD_DIR/better_sqlite3.node" - -echo "Done. Verify with: readelf -d \"$PKG_DIR/build/Release/better_sqlite3.node\" | grep libnode" diff --git a/scripts/prepare-local-mode-deps.js b/scripts/prepare-local-mode-deps.js new file mode 100644 index 0000000..39fd508 --- /dev/null +++ b/scripts/prepare-local-mode-deps.js @@ -0,0 +1,95 @@ +#!/usr/bin/env node +// Makes the two ESM-only server dependencies loadable by the embedded Node 18 +// (nodejs-mobile) runtime, which cannot require() ESM. Run AFTER +// `npm install` inside nodejs-assets/nodejs-project (re-run after any install). +// +// • kysely (used: routes/categories.cts -> db/kysely.cts) — bundled to a +// self-contained CommonJS file; its package entry is repointed at it. +// • openid-client (UNUSED in single-user local mode, but eagerly require()'d +// at boot via auth.cts/authOidc.cts -> oidcService.cts) — replaced with a +// CommonJS stub so boot doesn't crash. Any actual use throws. +// +// Both are loaded on the web app (Node 22, native ESM); this only affects the +// on-device copy. + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const esbuild = require('esbuild'); // from the mobile project's own node_modules + +const PROJECT = path.join(__dirname, '..', 'nodejs-assets', 'nodejs-project'); +const NM = path.join(PROJECT, 'node_modules'); + +function fail(msg) { + console.error('prepare-local-mode-deps: ' + msg); + process.exit(1); +} + +// ── kysely → CommonJS ──────────────────────────────────────────────────────── +(function bundleKysely() { + const dir = path.join(NM, 'kysely'); + if (!fs.existsSync(dir)) fail('kysely not installed — run `npm install` in nodejs-project first.'); + + const entry = path.join(dir, 'dist', 'index.js'); // ESM entry + const outfile = path.join(dir, 'dist', 'index.cjs'); + esbuild.buildSync({ + entryPoints: [entry], + bundle: true, + format: 'cjs', + platform: 'node', + target: 'node18', + outfile, + logLevel: 'silent', + }); + + const pkgPath = path.join(dir, 'package.json'); + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + pkg.main = './dist/index.cjs'; + pkg.exports = { + '.': { require: './dist/index.cjs', import: './dist/index.js', default: './dist/index.cjs' }, + './package.json': './package.json', + }; + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); + console.log(' kysely -> bundled to CommonJS (dist/index.cjs)'); +})(); + +// ── openid-client → CommonJS stub ──────────────────────────────────────────── +(function stubOpenidClient() { + const dir = path.join(NM, 'openid-client'); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync( + path.join(dir, 'package.json'), + JSON.stringify({ name: 'openid-client', version: '0.0.0-local-stub', main: 'index.js' }, null, 2) + '\n', + ); + fs.writeFileSync( + path.join(dir, 'index.js'), + "'use strict';\n" + + '// Local-mode stub. OIDC/SSO is not available when running on-device.\n' + + 'function notSupported() { throw new Error("OIDC is not available in local mode"); }\n' + + 'module.exports = new Proxy({}, { get: function () { return notSupported; } });\n', + ); + console.log(' openid-client -> CommonJS stub (OIDC disabled in local mode)'); +})(); + +// ── ICU-less \p{} regex compatibility ──────────────────────────────────────── +// nodejs-mobile's Node 18 ships WITHOUT ICU, so V8 cannot resolve Unicode +// property escapes (\p{...}) — any regex using them throws "Invalid property +// name" at compile time. path-to-regexp (express 5's router) uses +// \p{ID_Start}/\p{ID_Continue} at module load to validate route-param names, +// which crashes server boot. Rewrite to ASCII-safe classes; every route param +// in this app is ASCII (:id, :userId, …). (minimatch also contains \p{} but only +// as lazy POSIX-class strings that this app's globs never trigger — left as-is.) +(function patchPathToRegexp() { + const f = path.join(NM, 'path-to-regexp', 'dist', 'index.js'); + if (!fs.existsSync(f)) return; + let s = fs.readFileSync(f, 'utf8'); + const before = s; + s = s.replace(/\\p\{ID_Start\}/g, 'a-zA-Z').replace(/\\p\{ID_Continue\}/g, 'a-zA-Z0-9'); + if (s !== before) { + fs.writeFileSync(f, s); + console.log(' path-to-regexp -> ASCII identifier regex (ICU-less \\p{} fix)'); + } +})(); + +console.log('prepare-local-mode-deps: done.'); diff --git a/scripts/sync-nodejs-project.sh b/scripts/sync-nodejs-project.sh index aed332b..a0ea425 100755 --- a/scripts/sync-nodejs-project.sh +++ b/scripts/sync-nodejs-project.sh @@ -1,17 +1,20 @@ #!/usr/bin/env bash -# Copies the bill-tracker backend source into mobile/nodejs-assets/nodejs-project/server/ -# so it can be bundled into the Android app and run by nodejs-mobile in local mode. +# Copies the bill-tracker backend into mobile/nodejs-assets/nodejs-project/server/ +# so it can be bundled into the Android app and run by nodejs-mobile (Node 18) in +# local mode. +# +# The backend is now TypeScript (server.cts + .cts/.mts). Node 18 can't run TS, so +# this transpiles the server's own sources to CommonJS in place (keeping filenames; +# see scripts/transpile-node18.js). Dependencies run as-is on Node 18 and are NOT +# transpiled — install them separately (see the "Next" steps printed at the end). # # Usage: ./scripts/sync-nodejs-project.sh [--source /path/to/bill-tracker] -# Run from the mobile project root. -# -# --source defaults to ../bill-tracker (sibling directory). +# --source defaults to ../bill-tracker (sibling directory). Run from mobile root. set -euo pipefail MOBILE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -# Parse --source flag REPO_ROOT="" while [[ $# -gt 0 ]]; do case "$1" in @@ -19,14 +22,13 @@ while [[ $# -gt 0 ]]; do *) echo "Unknown arg: $1" >&2; exit 1 ;; esac done - if [[ -z "$REPO_ROOT" ]]; then REPO_ROOT="$(cd "$MOBILE_DIR/../bill-tracker" && pwd)" fi -if [[ ! -f "$REPO_ROOT/server.js" ]]; then - echo "Error: Bill Tracker source not found at $REPO_ROOT" >&2 - echo "Use --source /path/to/bill-tracker to specify the Bill Tracker project root." >&2 +if [[ ! -f "$REPO_ROOT/server.cts" ]]; then + echo "Error: Bill Tracker source not found at $REPO_ROOT (no server.cts)." >&2 + echo "Use --source /path/to/bill-tracker to specify the project root." >&2 exit 1 fi @@ -36,43 +38,41 @@ echo "Syncing backend source from $REPO_ROOT -> $DEST" rm -rf "$DEST" mkdir -p "$DEST" -cp "$REPO_ROOT/server.js" "$DEST/" +cp "$REPO_ROOT/server.cts" "$DEST/" -for dir in db routes services middleware utils workers; do - cp -R "$REPO_ROOT/$dir" "$DEST/$dir" +# Backend source trees. `types/` is type-only (erased by the transpile) but cheap +# insurance; `setup/` holds firstRun.cts (required by server.cts on empty DB). +for dir in db routes services middleware utils workers setup types; do + [[ -d "$REPO_ROOT/$dir" ]] && cp -R "$REPO_ROOT/$dir" "$DEST/$dir" done -# routes/user.js requires ../scripts/seedDemoData (relative to server/). +# routes/user.cts requires ../scripts/seedDemoData.cts (relative to server/). mkdir -p "$DEST/scripts" -cp "$REPO_ROOT/scripts/seedDemoData.js" "$DEST/scripts/" +cp "$REPO_ROOT/scripts/seedDemoData.cts" "$DEST/scripts/" -# Drop dev SQLite DB files and node_modules if they were copied along with db/. -rm -f "$DEST"/db/*.db "$DEST"/db/*.db-* "$DEST"/db/*.db-shm "$DEST"/db/*.db-wal +# Seed JSON read at first-run init: +# db/data/*.json -> carried with db/ above (database.cts reads them) +# docs/top_200_*.json -> db/migrations/versionedMigrations.cts reads ../../docs/ +mkdir -p "$DEST/docs" +cp "$REPO_ROOT/docs/top_200_us_subscriptions_researched_2026-06-06.json" "$DEST/docs/" + +# Drop dev SQLite DBs and any stray node_modules that rode along with db/. +rm -f "$DEST"/db/*.db "$DEST"/db/*.db-* 2>/dev/null || true rm -rf "$DEST/db/node_modules" -# Seed-data JSON files read by db/database.js during first-run initialization. -mkdir -p "$DEST/docs" -cp "$REPO_ROOT/docs/advisory_non_bill_transaction_filters_us_ms_5000.json" "$DEST/docs/" -cp "$REPO_ROOT/docs/top_200_us_subscriptions_researched_2026-06-06.json" "$DEST/docs/" -cp "$REPO_ROOT/docs/merchant_store_match_us_nems_online_5k_v0_2.json" "$DEST/docs/" +# Transpile the server's TypeScript sources to CommonJS in place (Node 18). +# Runs BEFORE dist/ is copied so the browser ESM bundle isn't touched. +node "$MOBILE_DIR/scripts/transpile-node18.js" "$DEST" -# nodejs-mobile-cordova 0.4.3 embeds Node 12.19, which doesn't support -# optional chaining (?.) / nullish coalescing (??) used in this project and -# in some npm dependencies (e.g. express-rate-limit). Transpile server-side -# .js files down to Node 12-compatible syntax in place. Runs before the -# frontend dist/ is copied below so its browser ESM bundle isn't rewritten -# to CJS (which breaks with "module is not defined" in the WebView). -node "$MOBILE_DIR/scripts/transpile-node12.js" "$DEST" - -# Built frontend, served as static files by server.js (path.join(__dirname, 'dist')). +# Built frontend, served as static files by server.cts (path.join(__dirname,'dist')). cp -R "$REPO_ROOT/dist" "$DEST/dist" -# Also transpile already-installed dependencies, if present (run -# `npm install` in nodejs-assets/nodejs-project after this script to pick up -# new/updated deps, then re-run this script to transpile them). -NODE_MODULES="$MOBILE_DIR/nodejs-assets/nodejs-project/node_modules" -if [ -d "$NODE_MODULES" ]; then - node "$MOBILE_DIR/scripts/transpile-node12.js" "$NODE_MODULES" -fi +cat <CJS, stub openid-client + 3. "$MOBILE_DIR/scripts/install-node18-runtime.sh" # (re)apply Node 18 runtime + bridge patch + 4. "$MOBILE_DIR/scripts/build-better-sqlite3-node18.sh" # better-sqlite3 for the Node 18 ABI +EOF diff --git a/scripts/transpile-node12.js b/scripts/transpile-node12.js deleted file mode 100644 index cb09ed6..0000000 --- a/scripts/transpile-node12.js +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env node -// Transpiles all .js files under a directory (in place) to syntax supported -// by nodejs-mobile-cordova's embedded Node 12.19 — primarily optional -// chaining (?.) and nullish coalescing (??) used by some npm dependencies -// and by this project's own source. -// -// Usage: node transpile-node12.js [ ...] - -'use strict'; - -const fs = require('fs'); -const path = require('path'); - -// Resolve esbuild from this project's own node_modules (no longer parent repo) -const esbuild = require('esbuild'); - -const SKIP_DIRS = new Set(['.bin']); - -function walk(dir, cb) { - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - if (SKIP_DIRS.has(entry.name)) continue; - const full = path.join(dir, entry.name); - if (entry.isSymbolicLink()) continue; - if (entry.isDirectory()) { - walk(full, cb); - } else if (entry.name.endsWith('.js') || entry.name.endsWith('.cjs') || entry.name === 'package.json') { - cb(full); - } - } -} - -let count = 0; -let pkgCount = 0; -for (const dir of process.argv.slice(2)) { - if (!fs.existsSync(dir)) continue; - walk(dir, (file) => { - if (path.basename(file) === 'package.json') { - // esbuild converts ESM (import/export) to CJS (module.exports) below, - // but Node still treats .js files as ES modules if the nearest - // package.json has "type": "module" — drop that so the converted - // CJS output is loaded correctly under Node 12. - const pkg = JSON.parse(fs.readFileSync(file, 'utf8')); - if (pkg.type === 'module') { - delete pkg.type; - fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n'); - pkgCount++; - } - return; - } - - const src = fs.readFileSync(file, 'utf8'); - let out; - try { - out = esbuild.transformSync(src, { target: 'node12', format: 'cjs', loader: 'js' }).code; - } catch (err) { - console.warn(`Skipping ${file}: ${err.message.split('\n')[0]}`); - return; - } - // Node 12 doesn't understand the "node:" builtin-module prefix - // (added in Node 14.18/16), used by some dependencies (e.g. express-rate-limit). - out = out.replace(/require\((['"])node:([a-z/_-]+)\1\)/g, "require($1$2$1)"); - - if (out !== src) { - fs.writeFileSync(file, out); - count++; - } - }); -} -console.log(`Transpiled ${count} file(s) and dropped "type": "module" from ${pkgCount} package.json file(s) for Node 12 compatibility.`); \ No newline at end of file diff --git a/scripts/transpile-node18.js b/scripts/transpile-node18.js new file mode 100644 index 0000000..3ed90fb --- /dev/null +++ b/scripts/transpile-node18.js @@ -0,0 +1,72 @@ +#!/usr/bin/env node +// Transpiles the Bill Tracker server's TypeScript sources to plain CommonJS for +// the embedded Node 18.20.4 runtime (nodejs-mobile). Node 18 runs modern JS +// syntax natively, so — unlike the old Node-12 pass — this ONLY strips +// TypeScript; it does not rewrite dependency syntax and does not touch +// node_modules (their JS runs as-is on Node 18). +// +// The server is CommonJS-with-TS: files use require()/module.exports plus +// `import type`/`as typeof import()` for types, and require() each other with +// EXPLICIT `.cts`/`.mts` extensions (115 .cts + 40 .mts call sites). We therefore +// transpile each .cts/.mts file IN PLACE, keeping its original name: esbuild +// strips the types and emits CJS, and Node 18's CommonJS loader loads unknown +// extensions (.cts/.mts) through the default `.js` handler as CommonJS — so every +// explicit-extension require() path keeps resolving with zero path rewriting. +// +// Usage: node transpile-node18.js [ ...] + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const esbuild = require('esbuild'); + +const SKIP_DIRS = new Set(['.bin', 'node_modules', 'dist']); + +function loaderFor(file) { + if (/\.(cts|mts|ts)$/.test(file)) return 'ts'; + if (/\.(cjs|js)$/.test(file)) return 'js'; + return null; +} + +function walk(dir, cb) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (SKIP_DIRS.has(entry.name)) continue; + if (entry.isSymbolicLink()) continue; + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walk(full, cb); + else if (loaderFor(entry.name)) cb(full); + } +} + +let count = 0; +let failed = 0; +for (const dir of process.argv.slice(2)) { + if (!fs.existsSync(dir)) continue; + walk(dir, (file) => { + const src = fs.readFileSync(file, 'utf8'); + let out; + try { + out = esbuild.transformSync(src, { + loader: loaderFor(file), + format: 'cjs', + target: 'node18', + sourcefile: file, + }).code; + } catch (err) { + console.error(`FAILED ${file}: ${err.message.split('\n')[0]}`); + failed++; + return; + } + if (out !== src) { + fs.writeFileSync(file, out); + count++; + } + }); +} + +if (failed) { + console.error(`Transpile finished with ${failed} failure(s).`); + process.exit(1); +} +console.log(`Transpiled ${count} file(s) to CommonJS for Node 18.`);