From dc6e01db14389cc2ce83061d0fcc6fe273486930 Mon Sep 17 00:00:00 2001 From: James Brumond Date: Sun, 23 Jul 2023 19:43:05 -0700 Subject: [PATCH] build sqlite3 store --- package-lock.json | 1224 +++++++++++++++++ package.json | 6 +- schemas/config.json | 27 +- src/http-web/authentication/login-callback.ts | 31 +- src/start.ts | 2 +- src/storage/config.ts | 4 +- src/storage/file/index.ts | 3 +- src/storage/file/users.ts | 16 +- src/storage/index.ts | 11 +- src/storage/provider.ts | 13 +- src/storage/sqlite3/config.ts | 6 + src/storage/sqlite3/db.ts | 17 + src/storage/sqlite3/index.ts | 71 + src/storage/sqlite3/migrate.ts | 131 ++ src/storage/sqlite3/pool.ts | 91 ++ src/storage/sqlite3/run.ts | 83 ++ src/storage/sqlite3/schema-utils.ts | 30 + src/storage/sqlite3/v1/migrate.ts | 113 ++ src/storage/sqlite3/v1/sessions.ts | 113 ++ src/storage/sqlite3/v1/settings.ts | 0 src/storage/sqlite3/v1/users.ts | 133 ++ src/utilities/bind.ts | 8 + src/utilities/types.ts | 20 + 23 files changed, 2120 insertions(+), 33 deletions(-) create mode 100644 src/storage/sqlite3/config.ts create mode 100644 src/storage/sqlite3/db.ts create mode 100644 src/storage/sqlite3/index.ts create mode 100644 src/storage/sqlite3/migrate.ts create mode 100644 src/storage/sqlite3/pool.ts create mode 100644 src/storage/sqlite3/run.ts create mode 100644 src/storage/sqlite3/schema-utils.ts create mode 100644 src/storage/sqlite3/v1/migrate.ts create mode 100644 src/storage/sqlite3/v1/sessions.ts create mode 100644 src/storage/sqlite3/v1/settings.ts create mode 100644 src/storage/sqlite3/v1/users.ts create mode 100644 src/utilities/bind.ts diff --git a/package-lock.json b/package-lock.json index ddb547a..4969271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,11 @@ "argon2": "^0.30.3", "fast-xml-parser": "^4.2.6", "fastify": "^4.19.2", + "generic-pool": "^3.9.0", "luxon": "^3.3.0", "openid-client": "^5.4.3", "pino": "^8.14.1", + "sqlite3": "^5.1.6", "yaml": "^2.3.1" }, "devDependencies": { @@ -95,6 +97,12 @@ "fastify-plugin": "^4.0.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -114,6 +122,30 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@phc/format": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", @@ -122,6 +154,15 @@ "node": ">=10" } }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/node": { "version": "20.4.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", @@ -160,6 +201,33 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -327,6 +395,47 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -335,6 +444,15 @@ "node": ">=10" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -402,6 +520,15 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -426,6 +553,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -434,6 +570,21 @@ "once": "^1.4.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -644,6 +795,14 @@ "node": ">=10" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -663,6 +822,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "optional": true + }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -732,6 +897,26 @@ "node": ">= 6" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "optional": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -744,6 +929,27 @@ "node": ">= 6" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -763,6 +969,30 @@ } ] }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -792,6 +1022,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "optional": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -808,11 +1044,23 @@ "node": ">=8" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "optional": true + }, "node_modules/jose": { "version": "4.14.4", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", @@ -892,6 +1140,45 @@ "semver": "bin/semver.js" } }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -928,6 +1215,131 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -967,6 +1379,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -991,6 +1412,91 @@ } } }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -1075,6 +1581,21 @@ "node": ">=8" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1258,6 +1779,25 @@ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz", "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1364,6 +1904,15 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1413,6 +1962,12 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, "node_modules/secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", @@ -1447,6 +2002,44 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "optional": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/sonic-boom": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", @@ -1463,6 +2056,57 @@ "node": ">= 10.x" } }, + "node_modules/sqlite3": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz", + "integrity": "sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^4.2.0", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/sqlite3/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -1584,6 +2228,24 @@ "node": ">=14.17" } }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1611,6 +2273,21 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -1712,6 +2389,12 @@ "fastify-plugin": "^4.0.0" } }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, "@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -1728,11 +2411,37 @@ "tar": "^6.1.11" } }, + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "optional": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, "@phc/format": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==" }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true + }, "@types/node": { "version": "20.4.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", @@ -1765,6 +2474,27 @@ "debug": "4" } }, + "agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "optional": true, + "requires": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -1878,11 +2608,54 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true + }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -1933,6 +2706,12 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "optional": true + }, "detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -1954,6 +2733,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1962,6 +2750,18 @@ "once": "^1.4.0" } }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -2137,6 +2937,11 @@ "wide-align": "^1.1.2" } }, + "generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2150,6 +2955,12 @@ "path-is-absolute": "^1.0.0" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "optional": true + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -2209,6 +3020,23 @@ } } }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "optional": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -2218,11 +3046,47 @@ "debug": "4" } }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "optional": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2246,6 +3110,12 @@ "p-is-promise": "^3.0.0" } }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "optional": true + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2256,11 +3126,23 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "optional": true + }, "jose": { "version": "4.14.4", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", @@ -2321,6 +3203,41 @@ } } }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2345,6 +3262,109 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==" }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -2374,6 +3394,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "optional": true + }, "node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -2387,6 +3413,75 @@ "whatwg-url": "^5.0.0" } }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "optional": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -2450,6 +3545,15 @@ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==" }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2591,6 +3695,22 @@ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz", "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2681,6 +3801,12 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2717,6 +3843,12 @@ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, "secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", @@ -2745,6 +3877,33 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true + }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "optional": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, "sonic-boom": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", @@ -2758,6 +3917,44 @@ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" }, + "sqlite3": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz", + "integrity": "sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^4.2.0", + "node-gyp": "8.x", + "tar": "^6.1.11" + }, + "dependencies": { + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + } + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "requires": { + "minipass": "^3.1.1" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -2853,6 +4050,24 @@ "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2880,6 +4095,15 @@ "webidl-conversions": "^3.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", diff --git a/package.json b/package.json index bf61ba9..8b19093 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "url": "git@git.jbrumond.me:templates/nodejs-typescript-service.git" }, "scripts": { - "tsc": "tsc --build" + "tsc": "tsc --build", + "clean:build": "rm -rf ./build/*", + "clean:data": "rm -rf ./data/*" }, "author": "James Brumond ", "license": "ISC", @@ -26,9 +28,11 @@ "argon2": "^0.30.3", "fast-xml-parser": "^4.2.6", "fastify": "^4.19.2", + "generic-pool": "^3.9.0", "luxon": "^3.3.0", "openid-client": "^5.4.3", "pino": "^8.14.1", + "sqlite3": "^5.1.6", "yaml": "^2.3.1" } } diff --git a/schemas/config.json b/schemas/config.json index 9766717..5a99767 100644 --- a/schemas/config.json +++ b/schemas/config.json @@ -219,7 +219,8 @@ "title": "Storage Config", "description": "Configuration for the main application data storage layer", "oneOf": [ - { "$ref": "#/$defs/file_storage_config" } + { "$ref": "#/$defs/file_storage_config" }, + { "$ref": "#/$defs/sqlite3_storage_config" } ] } }, @@ -248,6 +249,30 @@ "required": [ "engine" ] + }, + "sqlite3_storage_config": { + "type": "object", + "properties": { + "engine": { + "type": "string", + "const": "sqlite3" + }, + "pool_min": { + "description": "", + "type": "integer", + "minimum": 2, + "maximum": 100 + }, + "pool_max": { + "description": "", + "type": "integer", + "minimum": 2, + "maximum": 100 + } + }, + "required": [ + "engine" + ] } } } \ No newline at end of file diff --git a/src/http-web/authentication/login-callback.ts b/src/http-web/authentication/login-callback.ts index 99dc0cc..e6f8a4e 100644 --- a/src/http-web/authentication/login-callback.ts +++ b/src/http-web/authentication/login-callback.ts @@ -1,11 +1,14 @@ import * as sch from '../../utilities/json-schema'; -import { HttpConfig } from '../../http/server'; -import { HttpWebDependencies } from '../server'; import { render_login_page } from './login-page'; import { send_html_error } from '../../http/send-error'; import { redirect_200_refresh } from '../../http/redirects'; -import { FastifyInstance, FastifyRequest, RouteShorthandOptions } from 'fastify'; + +import type { HttpConfig } from '../../http/server'; +import type { HttpWebDependencies } from '../server'; +import type { HttpURL, Locale, Timezone } from '../../utilities/types'; +import type { FastifyInstance, FastifyRequest, RouteShorthandOptions } from 'fastify'; +import { UserinfoResponse } from 'openid-client'; export function register_login_callback_endpoint(http_server: FastifyInstance, conf: HttpConfig, deps: HttpWebDependencies) { const { logger, pkce_cookie, oidc, storage, snowflake, session, argon2 } = deps; @@ -77,24 +80,24 @@ export function register_login_callback_endpoint(http_server: FastifyInstance, c } log.debug({ oidc_subject: user_info.sub }, 'fetched user info; looking up local user data'); + log.debug({ user_info }, 'userinfo'); let user = await storage.get_user_by_oidc_sub(user_info.sub); if (! user) { log.debug('user does not have a local profile; creating a new one'); - // todo: handle missing fields // todo: handle non-unique usernames user = { id: snowflake.uid_str(), oidc_subject: user_info.sub, username: user_info.preferred_username, - locale: user_info.locale, - time_zone: user_info.zoneinfo, name: user_info.name, - picture: user_info.picture, - profile: user_info.profile, - website: user_info.website, + locale: get_locale(user_info), + timezone: get_zoneinfo(user_info), + picture: user_info.picture as HttpURL, + profile: user_info.profile as HttpURL, + website: user_info.website as HttpURL, }; await storage.create_user(user); @@ -111,3 +114,13 @@ export function register_login_callback_endpoint(http_server: FastifyInstance, c redirect_200_refresh(res, conf.exposed_url, 'Login successful'); }); } + +function get_locale(user_info: UserinfoResponse) : Locale { + const from_userinfo = user_info.locale?.replace('_', '-') as Locale; + return from_userinfo || 'en-US'; +} + +function get_zoneinfo(user_info: UserinfoResponse) : Timezone { + const from_userinfo = user_info.zoneinfo as Timezone; + return from_userinfo || 'Africa/Abidjan'; +} diff --git a/src/start.ts b/src/start.ts index b271e42..4f06edc 100644 --- a/src/start.ts +++ b/src/start.ts @@ -21,7 +21,7 @@ async function main() { // Create the logger and storage const logger = create_logger(conf.logging); - const storage = create_storage_provider(conf.storage); + const storage = create_storage_provider(conf.storage, logger.child({ logger: 'storage' })); // Create the metadata server const http_meta = create_http_metadata_server(conf.http_meta, { diff --git a/src/storage/config.ts b/src/storage/config.ts index d1d6660..5ee820e 100644 --- a/src/storage/config.ts +++ b/src/storage/config.ts @@ -1,10 +1,10 @@ import type { FileStorageConfig } from './file/config'; -// import type { SQLiteStorageConfig } from './sqlite/config'; +import type { SQLite3StorageConfig } from './sqlite3/config'; // import type { MariaDBStorageConfig } from './mariadb/config'; export type StorageConfig = FileStorageConfig - // | SQLiteStorageConfig + | SQLite3StorageConfig // | MariaDBStorageConfig ; diff --git a/src/storage/file/index.ts b/src/storage/file/index.ts index e4373bd..988907b 100644 --- a/src/storage/file/index.ts +++ b/src/storage/file/index.ts @@ -1,11 +1,12 @@ +import { pino } from 'pino'; import { make_data_dir } from '../files'; import { StorageProvider } from '../provider'; import { FileStorageConfig } from './config'; import { create_session, get_session, delete_session, cleanup_old_sessions } from './sessions'; import { get_user, create_user, update_user, delete_user, get_user_by_oidc_sub } from './users'; -export function create_file_storage_provider(conf: FileStorageConfig) : StorageProvider { +export function create_file_storage_provider(conf: FileStorageConfig, logger: pino.Logger) : StorageProvider { let status: 'ok' | 'warning' | 'updating' | 'unavailable' = 'unavailable'; const init_steps = [ diff --git a/src/storage/file/users.ts b/src/storage/file/users.ts index e3fe6e8..e791bdf 100644 --- a/src/storage/file/users.ts +++ b/src/storage/file/users.ts @@ -2,6 +2,8 @@ import type { UserData } from '../provider'; import type { Snowflake } from '../../utilities/snowflake-uid'; import { delete_data_file, read_data_file, write_data_file } from '../files'; +import { memo } from '../../utilities/memo'; +import { HttpURL, Locale, Timezone } from '../../utilities/types'; export async function get_user(user_id: Snowflake) : Promise { const data = await read_data_file(`users/${user_id}`, 'json'); @@ -32,11 +34,11 @@ interface UserJson { username: string; oidc_subject?: string; name?: string; - website?: string; - profile?: string; - picture?: string; - locale: string; - time_zone: string; + website?: HttpURL; + profile?: HttpURL; + picture?: HttpURL; + locale: Locale; + time_zone: Timezone; } function to_json(user: UserData) : UserJson { @@ -46,7 +48,7 @@ function to_json(user: UserData) : UserJson { oidc_subject: user.oidc_subject, name: user.name, locale: user.locale, - time_zone: user.time_zone, + time_zone: user.timezone, picture: user.picture, profile: user.profile, website: user.website, @@ -60,7 +62,7 @@ function from_json(json: UserJson) : UserData { oidc_subject: json.oidc_subject, name: json.name, locale: json.locale, - time_zone: json.time_zone, + timezone: json.time_zone, picture: json.picture, profile: json.profile, website: json.website, diff --git a/src/storage/index.ts b/src/storage/index.ts index defa24b..3ea8a82 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -2,15 +2,16 @@ import { StorageConfig } from './config'; import { StorageProvider } from './provider'; import { create_file_storage_provider } from './file'; -// import { create_sqlite_storage_provider } from './sqlite'; +import { create_sqlite3_storage_provider } from './sqlite3'; // import { create_mariadb_storage_provider } from './mariadb'; +import { pino } from 'pino'; export type * from './provider'; -export function create_storage_provider(conf: StorageConfig) : StorageProvider { +export function create_storage_provider(conf: StorageConfig, logger: pino.Logger) : StorageProvider { switch (conf.engine) { - case 'file': return create_file_storage_provider(conf); - // case 'sqlite': return create_sqlite_storage_provider(conf); - // case 'mariadb': return create_mariadb_storage_provider(conf); + case 'file': return create_file_storage_provider(conf, logger); + case 'sqlite3': return create_sqlite3_storage_provider(conf, logger); + // case 'mariadb': return create_mariadb_storage_provider(conf, logger); } } diff --git a/src/storage/provider.ts b/src/storage/provider.ts index 91eb90f..5dac8b3 100644 --- a/src/storage/provider.ts +++ b/src/storage/provider.ts @@ -1,5 +1,6 @@ import type { Snowflake } from '../utilities/snowflake-uid'; +import { HttpURL, Locale, Timezone } from '../utilities/types'; export interface StorageProvider { readonly ready: Promise; @@ -23,7 +24,7 @@ export interface StorageProvider { } export interface StorageStatus { - engine: 'file' | 'mysql'; + engine: 'file' | 'sqlite3' | 'mysql'; status: 'ok' | 'warning' | 'updating' | 'unavailable'; } @@ -40,9 +41,9 @@ export interface UserData { username: string; oidc_subject?: string; name?: string; - website?: string; - profile?: string; - picture?: string; - locale: string; - time_zone: string; + website?: HttpURL; + profile?: HttpURL; + picture?: HttpURL; + locale: Locale; + timezone: Timezone; } diff --git a/src/storage/sqlite3/config.ts b/src/storage/sqlite3/config.ts new file mode 100644 index 0000000..08fe6bb --- /dev/null +++ b/src/storage/sqlite3/config.ts @@ -0,0 +1,6 @@ + +export interface SQLite3StorageConfig { + engine: 'sqlite3'; + pool_min: number; + pool_max: number; +} diff --git a/src/storage/sqlite3/db.ts b/src/storage/sqlite3/db.ts new file mode 100644 index 0000000..63d622c --- /dev/null +++ b/src/storage/sqlite3/db.ts @@ -0,0 +1,17 @@ + +import { pino } from 'pino'; +import { Pool } from 'generic-pool'; +import { SQLite3StorageConfig } from './config'; +import { Database } from 'sqlite3'; + +export type DBPool = Pool; + +export interface DB { + conf: SQLite3StorageConfig; + logger: pino.Logger; + + // Database pools + settings: DBPool; + users: DBPool; + sessions: DBPool; +} diff --git a/src/storage/sqlite3/index.ts b/src/storage/sqlite3/index.ts new file mode 100644 index 0000000..0fd1b44 --- /dev/null +++ b/src/storage/sqlite3/index.ts @@ -0,0 +1,71 @@ + +import { DB } from './db'; +import { pino } from 'pino'; +import { Pool } from 'generic-pool'; +import { StorageProvider } from '../provider'; +import { create_sqlite_pool } from './pool'; +import { SQLite3StorageConfig } from './config'; +import { bind_first_param } from '../../utilities/bind'; + +import { create_session, get_session, delete_session, cleanup_old_sessions } from './v1/sessions'; +import { get_user, create_user, update_user, delete_user, get_user_by_oidc_sub } from './v1/users'; +import { bring_db_schema_up_to_date } from './migrate'; + +export function create_sqlite3_storage_provider(conf: SQLite3StorageConfig, logger: pino.Logger) : StorageProvider { + let status: 'ok' | 'warning' | 'updating' | 'unavailable' = 'unavailable'; + + const db: DB = { + conf, + logger, + settings: null, + users: null, + sessions: null, + }; + + db.settings = create_sqlite_pool('settings.db', db); + db.users = create_sqlite_pool('users.db', db); + db.sessions = create_sqlite_pool('sessions.db', db); + + const ready = bring_db_schema_up_to_date(db).then(() => { + status = 'ok'; + }); + + return { + ready, + + status() { + return { + engine: 'sqlite3', + status: status, + pools: { + settings: pool_stats(db.settings), + users: pool_stats(db.users), + sessions: pool_stats(db.sessions), + }, + }; + }, + + // Login Sessions + get_session: bind_first_param(get_session, db), + create_session: bind_first_param(create_session, db), + delete_session: bind_first_param(delete_session, db), + cleanup_old_sessions: bind_first_param(cleanup_old_sessions, db), + + // Users + get_user: bind_first_param(get_user, db), + get_user_by_oidc_sub: bind_first_param(get_user_by_oidc_sub, db), + create_user: bind_first_param(create_user, db), + update_user: bind_first_param(update_user, db), + delete_user: bind_first_param(delete_user, db), + }; + + function pool_stats(pool: Pool) { + return { + min: pool.min, + max: pool.max, + available: pool.available, + borrowed: pool.borrowed, + pending: pool.pending, + }; + } +} diff --git a/src/storage/sqlite3/migrate.ts b/src/storage/sqlite3/migrate.ts new file mode 100644 index 0000000..a08d3e1 --- /dev/null +++ b/src/storage/sqlite3/migrate.ts @@ -0,0 +1,131 @@ + +import type { DB } from './db'; +import type { ISOTimestamp } from '../../utilities/types'; +import type * as sqlite3 from 'sqlite3'; + +import { get_one, run } from './run'; +import { type } from './schema-utils'; +import { migrate_v0_to_v1 } from './v1/migrate'; + +const supported_db_version = 1; + +export let updating = false; + +interface Migration { + (db: DB): Promise; +} + +/** + * Ensures that the database files exist and are updated to their latest schema versions + */ +export async function bring_db_schema_up_to_date(db: DB, no_update = false) { + updating = true; + const log = db.logger.child({ source: 'storage/sqlite3/migrate' }); + const conn = await db.settings.acquire(); + + log.info(`checking for any needed db updates`); + + await begin_transaction(db, conn); + await create_table_migration_history(db, conn); + + let { version: db_version } = await get_current_schema_version(db, conn) || { version: 0 }; + + if (db_version === supported_db_version) { + log.info(`db already up to date`); + await commit(db, conn); + await db.settings.release(conn); + updating = false; + return; + } + + if (db_version > supported_db_version || db_version < 0) { + console.error('settings.db seems to contain an unknown or unsupported version'); + await commit(db, conn); + await db.settings.release(conn); + process.exit(1); + } + + if (no_update) { + console.error('settings.db is out of date, but no_update flag is set'); + await commit(db, conn); + await db.settings.release(conn); + process.exit(1); + } + + log.info(`db needs updates`, { db_version, new_version: supported_db_version }); + + while (db_version < supported_db_version) { + const now = (new Date).toISOString() as ISOTimestamp; + log.info(`performing update from v${db_version} to v${db_version + 1}...`); + await migrations[db_version](db); + db_version++; + await append_migration(db, conn, db_version, now); + } + + await commit(db, conn); + await db.settings.release(conn); + updating = false; +} + +const migrations: Migration[] = [ + migrate_v0_to_v1, + // migrate_v1_to_v2, +]; + + + + + + + +// ===== Migration History Table ===== + +const sql_create_table_migration_history = ` +create table if not exists migration_history ( + id ${type.rowid} primary key, + version int not null, + migrated_at datetime not null +)`; + +async function create_table_migration_history(db: DB, conn: sqlite3.Database) { + await run(db, conn, sql_create_table_migration_history); +} + +const sql_begin_transaction = ` +begin transaction +`; + +async function begin_transaction(db: DB, conn: sqlite3.Database) { + await run(db, conn, sql_begin_transaction); +} + +const sql_commit = ` +commit +`; + +async function commit(db: DB, conn: sqlite3.Database) { + await run(db, conn, sql_commit); +} + +const sql_get_current_schema_version = ` +select + hist.version as version +from migration_history hist +order by id desc +limit 1 +`; + +function get_current_schema_version(db: DB, conn: sqlite3.Database) { + return get_one<{ version: number }>(db, conn, sql_get_current_schema_version); +} + +const sql_append_migration = ` +insert into migration_history + (version, migrated_at) +values + (?, ?) +`; + +async function append_migration(db: DB, conn: sqlite3.Database, version: number, migrated_at: ISOTimestamp) { + await run(db, conn, sql_append_migration, [ version, migrated_at ]); +} diff --git a/src/storage/sqlite3/pool.ts b/src/storage/sqlite3/pool.ts new file mode 100644 index 0000000..3ff96e7 --- /dev/null +++ b/src/storage/sqlite3/pool.ts @@ -0,0 +1,91 @@ + +import { DB } from './db'; +import { run } from './run'; +import { data_path } from '../files'; +import { promises as fs } from 'fs'; +import { createPool, Factory, Pool } from 'generic-pool'; +import * as sqlite3 from 'sqlite3'; + +let next_conn_id = 1; + +export const db_info_map = new Map(); + +export type DBPool = Pool; + +export interface DBInfo { + file: string; + mode: number; + conn_id: number; +} + +export function create_sqlite_pool(file: string, db: DB) : DBPool { + const path = data_path(file); + const mode = sqlite3.OPEN_CREATE | sqlite3.OPEN_READWRITE; + const factory: Factory = { + create() { + return open(db, path, mode); + }, + destroy(conn: sqlite3.Database) { + return close(db, conn); + }, + }; + + const pool = createPool(factory, { + min: db.conf.pool_min, + max: db.conf.pool_max, + }); + + return pool; +} + +function open(db: DB, file: string, mode: number) : Promise { + const conn_id = next_conn_id++; + const db_info: DBInfo = { file, mode, conn_id }; + const log = db.logger.child(db_info); + + log.info('opening database connection'); + + return new Promise((resolve, reject) => { + const conn = new sqlite3.Database(file, mode, async (error) => { + if (error) { + log.error(error); + return reject(error); + } + + db_info_map.set(conn, db_info); + + log.debug('database open'); + log.debug('confirming file permissions = 0600'); + + // Ensure the file is not accessible to anyone but the server user + await fs.chmod(file, 0o600); + + log.debug('enabling foreign keys'); + + // Enable foreign keys + await run(db, conn, 'PRAGMA foreign_keys = ON'); + + log.info('ready'); + resolve(conn); + }); + }); +} + +function close(db: DB, conn: sqlite3.Database) : Promise { + const db_info = db_info_map.get(conn); + const log = db.logger.child(db_info); + + log.info('closing database connection'); + + return new Promise((resolve, reject) => { + conn.close((error) => { + if (error) { + return reject(error); + } + + log.info('closed'); + resolve(); + }); + }); +} + diff --git a/src/storage/sqlite3/run.ts b/src/storage/sqlite3/run.ts new file mode 100644 index 0000000..b669471 --- /dev/null +++ b/src/storage/sqlite3/run.ts @@ -0,0 +1,83 @@ + +import { DB } from './db'; +import { db_info_map } from './pool'; +import * as sqlite3 from 'sqlite3'; + +let next_query_id = 1; + +export function run(db: DB, conn: sqlite3.Database, query: string, params: any[] | object = [ ]) { + const query_id = next_query_id++; + const info = Object.assign({ query_id }, db_info_map.get(conn)); + const log = db.logger.child(info); + const start = Date.now(); + + query = sql(query); + + return new Promise((resolve, reject) => { + log.trace(`run: ${query}`); + + conn.run(query, params, function(error) { + if (error) { + log.error('run failed: ' + error.stack); + return reject(error); + } + + log.debug({ duration: Date.now() - start }, `run complete: ${trunc(query)}`); + resolve(this); + }); + }); +} + +export function get_one(db: DB, conn: sqlite3.Database, query: string, params: any[] | object = [ ]) { + const query_id = next_query_id++; + const info = Object.assign({ query_id }, db_info_map.get(conn)); + const log = db.logger.child(info); + const start = Date.now(); + + query = sql(query); + + return new Promise((resolve, reject) => { + log.trace(`get_one: ${query}`); + + conn.get(query, params, function(error, row) { + if (error) { + log.error('get_one failed: ' + error.stack); + return reject(error); + } + + log.debug({ duration: Date.now() - start }, `get_one complete: ${trunc(query)}`); + resolve(row as any); + }); + }); +} + +export function get_all(db: DB, conn: sqlite3.Database, query: string, params: any[] | object = [ ]) { + const query_id = next_query_id++; + const info = Object.assign({ query_id }, db_info_map.get(conn)); + const log = db.logger.child(info); + const start = Date.now(); + + query = sql(query); + + return new Promise((resolve, reject) => { + log.trace(`get_all: ${query}`); + + conn.all(query, params, function(error, rows) { + if (error) { + log.error('get_all failed: ' + error.stack); + return reject(error); + } + + log.debug({ duration: Date.now() - start }, `get_all complete: ${trunc(query)}`); + resolve(rows as any[]); + }); + }); +} + +function trunc(sql: string) { + return `${sql.slice(0, 20)}...`; +} + +export function sql(query: string) { + return query.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); +} diff --git a/src/storage/sqlite3/schema-utils.ts b/src/storage/sqlite3/schema-utils.ts new file mode 100644 index 0000000..345e57a --- /dev/null +++ b/src/storage/sqlite3/schema-utils.ts @@ -0,0 +1,30 @@ + +import { run } from './run'; +import type { DB } from './db'; +import type * as sqlite3 from 'sqlite3'; + +export const type = { + rowid: 'integer' as const, + url: 'varchar(1000)' as const, + snowflake: 'integer' as const, + uuid: 'varchar(50)' as const, + ref_value: 'varchar(64)' as const, + bool: 'tinyint unsigned' as const, +}; + +export async function create_ref_table(db: DB, conn: sqlite3.Database, table_name: string, values: string[]) { + const sql_create_table = ` + create table if not exists ${table_name} ( + value ${type.ref_value} not null primary key + ) + `; + + const sql_insert_values = ` + insert into ${table_name} + (value) + values ${values.map(() => `(?)`).join(', ')} + `; + + await run(db, conn, sql_create_table); + await run(db, conn, sql_insert_values, values); +} diff --git a/src/storage/sqlite3/v1/migrate.ts b/src/storage/sqlite3/v1/migrate.ts new file mode 100644 index 0000000..dc480a4 --- /dev/null +++ b/src/storage/sqlite3/v1/migrate.ts @@ -0,0 +1,113 @@ + +import { run } from '../run'; +import { type } from '../schema-utils'; +import type { DB } from '../db'; +import type * as sqlite3 from 'sqlite3'; +import type { Snowflake } from '../../../utilities/snowflake-uid'; + +export async function migrate_v0_to_v1(db: DB) { + await Promise.all([ + // migrate_v1_settings(db), + migrate_v1_users(db), + migrate_v1_sessions(db), + ]); +} + + + +// ===== Settings Database ===== + +async function migrate_v1_settings(db: DB) { + const conn = await db.settings.acquire(); + + try { + await create_table_settings(db, conn); + } + + finally { + await db.settings.release(conn); + } +} + +const sql_create_table_settings = ` +create table if not exists settings ( + // +)`; + +async function create_table_settings(db: DB, conn: sqlite3.Database) { + await run(db, conn, sql_create_table_settings); +} + + + +// ===== Users Database ===== + +async function migrate_v1_users(db: DB) { + const conn = await db.users.acquire(); + + try { + await create_table_users(db, conn); + } + + finally { + await db.users.release(conn); + } +} + +const sql_create_table_users = ` +create table if not exists users ( + id ${type.rowid} primary key, + username text not null, + oidc_subject text not null, + name text, + locale varchar(20) not null default 'en-US', + timezone text not null default 'Africa/Abidjan', + website ${type.url}, + profile ${type.url}, + picture ${type.url}, + + unique(username), + unique(oidc_subject) +)`; + +async function create_table_users(db: DB, conn: sqlite3.Database) { + await run(db, conn, sql_create_table_users); +} + + + +// ===== Sessions Database ===== + +async function migrate_v1_sessions(db: DB) { + const conn = await db.sessions.acquire(); + + try { + await create_table_sessions(db, conn); + } + + finally { + await db.sessions.release(conn); + } +} + +export interface SessionRow { + prefix: string; + key_hash: string; + user_id: Snowflake; + started: string; + expires: string; +} + +const sql_create_table_sessions = ` +create table if not exists sessions ( + prefix varchar(16) not null primary key, + key_hash text not null, + user_id ${type.rowid}, + started datetime not null, + expires datetime not null +)`; + +async function create_table_sessions(db: DB, conn: sqlite3.Database) { + await run(db, conn, sql_create_table_sessions); +} + diff --git a/src/storage/sqlite3/v1/sessions.ts b/src/storage/sqlite3/v1/sessions.ts new file mode 100644 index 0000000..72c58b9 --- /dev/null +++ b/src/storage/sqlite3/v1/sessions.ts @@ -0,0 +1,113 @@ + +import type { DB } from '../db'; +import type { SessionData } from '../../provider'; +import type { ISOTimestamp } from '../../../utilities/types'; +import type { Snowflake } from '../../../utilities/snowflake-uid'; + +import { get_one, run } from '../run'; + +export interface SessionRow { + prefix: string; + key_hash: string; + user_id: Snowflake; + started: ISOTimestamp; + expires: ISOTimestamp; +} + +const sql_get_session = ` +select + session.prefix as prefix, + session.key_hash as key_hash, + session.user_id as user_id, + session.started as started, + session.expires as expires +from sessions session +`; + +export async function get_session(db: DB, prefix: string) { + const conn = await db.sessions.acquire(); + + try { + const data = await get_one(db, conn, sql_get_session + 'where session.prefix = ?', [ prefix ]); + + if (! data) { + return null; + } + + return from_db_row(data); + } + + finally { + db.sessions.release(conn); + } +} + +const sql_create_session = ` +insert into sessions + (prefix, key_hash, user_id, started, expires) +values + (?, ?, ?, ?, ?) +`; + +export async function create_session(db: DB, data: SessionData) { + const conn = await db.sessions.acquire(); + + try { + await run(db, conn, sql_create_session, [ + data.prefix, + data.key_hash, + data.user_id, + data.started, + data.expires, + ]); + } + + finally { + db.sessions.release(conn); + } +} + +const sql_delete_session = ` +delete from sessions +where prefix = ? +`; + +export async function delete_session(db: DB, prefix: string) { + const conn = await db.sessions.acquire(); + + try { + await run(db, conn, sql_delete_session, [ prefix ]); + } + + finally { + db.sessions.release(conn); + } +} + +const sql_cleanup_old_sessions = ` +delete from sessions +where expires <= ? +`; + +export async function cleanup_old_sessions(db: DB) { + const now = (new Date).toISOString(); + const conn = await db.sessions.acquire(); + + try { + await run(db, conn, sql_cleanup_old_sessions, [ now ]); + } + + finally { + db.sessions.release(conn); + } +} + +function from_db_row(row: SessionRow) : SessionData { + return { + prefix: row.prefix, + key_hash: row.key_hash, + user_id: row.user_id, + started: new Date(Date.parse(row.started)), + expires: new Date(Date.parse(row.expires)), + }; +} diff --git a/src/storage/sqlite3/v1/settings.ts b/src/storage/sqlite3/v1/settings.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/storage/sqlite3/v1/users.ts b/src/storage/sqlite3/v1/users.ts new file mode 100644 index 0000000..0c3b57d --- /dev/null +++ b/src/storage/sqlite3/v1/users.ts @@ -0,0 +1,133 @@ + +import type { DB } from '../db'; +import type { UserData } from '../../provider'; +import type { Snowflake } from '../../../utilities/snowflake-uid'; +import type { HttpURL, Locale, Timezone } from '../../../utilities/types'; + +import { get_one, run } from '../run'; + +export interface UserRow { + id: Snowflake; + username: string; + oidc_subject?: string; + name?: string; + website?: HttpURL; + profile?: HttpURL; + picture?: HttpURL; + locale: Locale; + timezone: Timezone; +} + +const sql_get_user = ` +select + user.id as id, + user.username as username, + user.oidc_subject as oidc_subject, + user.name as name, + user.website as website, + user.profile as profile, + user.picture as picture, + user.locale as locale, + user.timezone as timezone +from users user +`; + +export async function get_user(db: DB, user_id: Snowflake) : Promise { + const conn = await db.users.acquire(); + + try { + const data = await get_one(db, conn, sql_get_user + 'where user.id = ?', [ user_id ]); + + if (! data) { + return null; + } + + return from_db_row(data); + } + + finally { + db.users.release(conn); + } +} + +export async function get_user_by_oidc_sub(db: DB, user_sub: string) : Promise { + const conn = await db.users.acquire(); + + try { + const data = await get_one(db, conn, sql_get_user + 'where user.oidc_subject = ?', [ user_sub ]); + + if (! data) { + return null; + } + + return from_db_row(data); + } + + finally { + db.users.release(conn); + } +} + +const sql_create_user = ` +insert into users + (id, username, oidc_subject, name, website, profile, picture, locale, timezone) +values + (?, ?, ?, ?, ?, ?, ?, ?, ?) +`; + +export async function create_user(db: DB, data: UserData) { + const conn = await db.users.acquire(); + + try { + await run(db, conn, sql_create_user, [ + data.id, + data.username, + data.oidc_subject, + data.name, + data.website, + data.profile, + data.picture, + data.locale, + data.timezone, + ]); + } + + finally { + db.users.release(conn); + } +} + +export async function update_user(db: DB, user_id: Snowflake, data: UserData) { + // todo: update_user +} + +const sql_delete_user = ` +delete from users +where id = ? +`; + +export async function delete_user(db: DB, user_id: Snowflake) { + const conn = await db.users.acquire(); + + try { + await run(db, conn, sql_delete_user, [ user_id ]); + } + + finally { + db.users.release(conn); + } +} + +function from_db_row(json: UserRow) : UserData { + return { + id: json.id, + username: json.username, + oidc_subject: json.oidc_subject, + name: json.name, + locale: json.locale, + timezone: json.timezone, + picture: json.picture, + profile: json.profile, + website: json.website, + }; +} diff --git a/src/utilities/bind.ts b/src/utilities/bind.ts new file mode 100644 index 0000000..1d1de86 --- /dev/null +++ b/src/utilities/bind.ts @@ -0,0 +1,8 @@ + +import type { FirstParam, Func, NonFirstParams } from './types'; + +export function bind_first_param(func: F, first: FirstParam) : (...args: NonFirstParams) => ReturnType { + return function (...args: NonFirstParams) : ReturnType { + return func(first, ...args); + }; +} diff --git a/src/utilities/types.ts b/src/utilities/types.ts index 25a2d46..d7797ef 100644 --- a/src/utilities/types.ts +++ b/src/utilities/types.ts @@ -2,3 +2,23 @@ export type Func = (...args: any[]) => T; export type Params = T extends (...args: infer P) => any ? P : never; + +export type FirstParam = T extends (first: infer F, ...args: infer P) => any ? F : never; + +export type NonFirstParams = T extends (first: infer F, ...args: infer P) => any ? P : never; + +export type UUID = `${string}-${string}-${string}-${string}-${string}`; + +export type ISODate = `${number}-${number}-${number}`; + +export type ISOTime = `${number}:${number}:${number}.${number}`; + +export type ISOZoneOffset = 'Z' | `${'+' | '-' | ''}${number}:${number}`; + +export type ISOTimestamp = `${ISODate}T${ISOTime}${ISOZoneOffset}`; + +export type Timezone = `${string}/${string}`; + +export type Locale = `${string}-${string}`; + +export type HttpURL = `http${'s' | ''}://${string}`;