From f0bf29c75b0fd405ff92fa76f058e61162b87e43 Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Mon, 13 May 2024 09:26:31 -0700 Subject: [PATCH] update dependencies and devDependencies, address lint issues --- eslint.config.js | 108 +- package-lock.json | 1197 +++++++++++------ package.json | 46 +- src/chores.js | 4 +- src/common.js | 55 +- src/db/abstract.js | 329 ++--- src/db/schema-version-helper.js | 54 +- src/db/sqlite/index.js | 8 +- src/logger/data-sanitizers.js | 33 +- src/manager.js | 266 ++-- src/service.js | 148 +- src/template/admin-html.js | 73 +- src/template/admin-maintenance-html.js | 38 +- src/template/admin-ticket-html.js | 33 +- src/template/authorization-error-html.js | 18 +- src/template/authorization-request-html.js | 142 +- src/template/root-html.js | 21 +- src/template/template-helper.js | 16 +- test/src/chores.js | 2 - test/src/db/abstract.js | 4 +- test/src/db/factory.js | 2 +- test/src/db/integration.js | 5 +- test/src/db/postgres.js | 10 +- test/src/db/schema-version-helper.js | 7 +- test/src/db/sqlite.js | 20 +- test/src/logger.js | 6 +- test/src/manager.js | 9 +- test/src/service.js | 8 +- test/src/template/admin-html.js | 2 +- test/src/template/admin-ticket-html.js | 2 +- test/src/template/authorization-error-html.js | 2 +- .../template/authorization-request-html.js | 2 +- test/stub-db.js | 3 +- test/stub-logger.js | 4 +- 34 files changed, 1533 insertions(+), 1144 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index e02ca72..fcda56f 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,111 +1,7 @@ 'use strict'; -const globals = require('globals'); -const js = require('@eslint/js'); -const node = require('eslint-plugin-n'); -const security = require('eslint-plugin-security'); -const sonarjs = require('eslint-plugin-sonarjs'); -const { FlatCompat } = require('@eslint/eslintrc'); -const compat = new FlatCompat(); +const squeepConfig = require('@squeep/eslint-config'); module.exports = [ - js.configs.recommended, - ...compat.config(node.configs.recommended), - security.configs.recommended, - ...compat.config(sonarjs.configs.recommended), - { - files: [ '**/*.js' ], - plugins: { - node, - security, - sonarjs, - }, - languageOptions: { - ecmaVersion: 2023, - sourceType: 'script', - }, - rules: { - 'array-element-newline': [ - 'error', - 'consistent', - ], - 'arrow-parens': [ - 'error', - 'always', - ], - 'arrow-spacing': [ - 'error', - { - 'after': true, - 'before': true, - }, - ], - 'block-scoped-var': 'error', - 'block-spacing': 'error', - 'brace-style': 'error', - 'callback-return': 'error', - 'camelcase': 'error', - 'class-methods-use-this': 'error', - 'comma-dangle': [ - 'error', - 'always-multiline', - ], - 'comma-spacing': [ - 'error', - { - 'after': true, - 'before': false, - }, - ], - 'comma-style': [ - 'error', - 'last', - ], - 'indent': [ - 'warn', - 2, - { - 'SwitchCase': 1, - }, - ], - 'sonarjs/cognitive-complexity': 'warn', - 'sonarjs/no-duplicate-string': 'warn', - 'keyword-spacing': 'error', - 'linebreak-style': [ - 'error', - 'unix', - ], - 'no-unused-vars': [ - 'error', { - 'varsIgnorePattern': '^_', - }, - ], - 'object-curly-spacing': [ - 'error', - 'always', - ], - 'prefer-const': 'error', - 'quotes': [ - 'error', - 'single', - ], - 'semi': [ - 'error', - 'always', - ], - 'strict': 'error', - 'vars-on-top': 'error', - }, - }, - { - files: ['test/**'], - languageOptions: { - globals: { - ...globals.mocha, - }, - }, - rules: { - "n/no-unpublished-require": "off", - }, - }, + ...squeepConfig, ]; diff --git a/package-lock.json b/package-lock.json index b28df97..74f36d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,45 +10,32 @@ "license": "ISC", "dependencies": { "@squeep/amqp-helper": "git+https://git.squeep.com/squeep-amqp-helper#v1.0.0", - "@squeep/api-dingus": "^2.1.0", - "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.4.0", + "@squeep/api-dingus": "^2", + "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.5.0", "@squeep/chores": "git+https://git.squeep.com/squeep-chores/#v1.0.1", - "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0", - "@squeep/indieauth-helper": "^1.4.1", - "@squeep/logger-json-console": "^3.0.2", - "@squeep/mystery-box": "^2.0.2", - "@squeep/resource-authentication-module": "git+https://git.squeep.com/squeep-resource-authentication-module#v1.0.1", - "@squeep/roman": "^1.0.1", - "@squeep/web-linking": "^1.0.8", - "better-sqlite3": "^9.4.5", - "pg-promise": "^11.6.0", - "uuid": "^9.0.1" + "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.1", + "@squeep/indieauth-helper": "^1.4", + "@squeep/logger-json-console": "^3", + "@squeep/mystery-box": "^2", + "@squeep/roman": "^1", + "@squeep/web-linking": "^1", + "better-sqlite3": "^9", + "pg-promise": "^11", + "uuid": "^9" }, "devDependencies": { + "@squeep/eslint-config": "^1", "@squeep/test-helper": "git+https://git.squeep.com/squeep-test-helper#v1.0.1", - "eslint": "^8.57.0", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-security": "^2.1.1", - "eslint-plugin-sonarjs": "^0.25.1", - "html-validate": "^8.18.1", - "mocha": "^10.4.0", - "mocha-steps": "^1.3.0", - "nyc": "^15.1.0", - "pre-commit": "^1.2.2", - "sinon": "^17.0.1" + "eslint": "^9", + "html-validate": "^8", + "mocha": "^10", + "mocha-steps": "^1", + "nyc": "^15", + "pre-commit": "^1", + "sinon": "^17" }, "engines": { - "node": "^14 >=14.18 || >=15.7" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=16.9" } }, "node_modules/@acuminous/bitsyntax": { @@ -100,21 +87,21 @@ } }, "node_modules/@babel/core": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", - "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.4", + "@babel/generator": "^7.24.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.4", - "@babel/parser": "^7.24.4", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -145,12 +132,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "dev": true, "dependencies": { - "@babel/types": "^7.24.0", + "@babel/types": "^7.24.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -231,16 +218,16 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -250,24 +237,24 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -283,9 +270,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -301,26 +288,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", - "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", "dev": true, "dependencies": { "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.5", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -401,9 +388,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -427,19 +414,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -457,19 +444,36 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.43.0.tgz", + "integrity": "sha512-Q1CnsQrytI3TlCB1IVWXWeqUIPGVEKGaE7IbVdt13Nq/3i0JESAkQQERrfiQkmlpijl+++qyqPgaS31Bvc1jRQ==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.56.5", + "@types/estree": "^1.0.5", + "@typescript-eslint/types": "^7.2.0", + "comment-parser": "1.4.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -485,6 +489,18 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", @@ -495,15 +511,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz", + "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -511,19 +527,31 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.2.0.tgz", + "integrity": "sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@html-validate/stylish": { @@ -539,12 +567,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -571,6 +599,19 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.2.4.tgz", + "integrity": "sha512-Ttl/jHpxfS3st5sxwICYfk4pOH0WrLI1SpW283GgQL7sCWU7EHIOhX4b4fkIxr3tkfzwg8+FNojtzsIEE7Ecgg==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -797,6 +838,18 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -868,11 +921,11 @@ } }, "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-6.3.0.tgz", + "integrity": "sha512-bOSPck7aIJjASXIg1qvXSIjXhVBpIEKdl2Wxg4pVqoTRPL8wWExKBrnGIh6CEnhkFQHfc36k7APhO3uXV4g5xg==", "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sindresorhus/is?sponsor=1" @@ -925,7 +978,6 @@ "node_modules/@squeep/amqp-helper": { "version": "1.0.0", "resolved": "git+https://git.squeep.com/squeep-amqp-helper#174280d3f44ba13dac0b26d42d968189a4f4fa93", - "license": "ISC", "dependencies": { "amqplib": "^0.10.3" }, @@ -934,31 +986,31 @@ } }, "node_modules/@squeep/api-dingus": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@squeep/api-dingus/-/api-dingus-2.1.0.tgz", - "integrity": "sha512-SCLPHbSHTz5en5XO8IMyTLr6R+0jIDpL3dSxS3e4XLC3521LzorNywGteMdnZKhwXwahjf4XngxNzmO0kFp6Kw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@squeep/api-dingus/-/api-dingus-2.1.1.tgz", + "integrity": "sha512-iae4nc0mZ2sBR0iHIOu/loqaXSFmsxGdFOVjE0wfzGY4QzvHqLeVgM8VnAczFtCOM8bOqe8RtcvVkN7pDM7TWw==", "dependencies": { - "@squeep/log-helper": "^1.0.0", + "@squeep/log-helper": "^1", "mime-db": "^1.52.0", "uuid": "^9.0.1" }, "engines": { - "node": ">=14" + "node": ">=14.13.1" } }, "node_modules/@squeep/authentication-module": { - "version": "1.4.0", - "resolved": "git+https://git.squeep.com/squeep-authentication-module/#9c604adfcde56e35767e3eba70890308ec2d3110", - "license": "ISC", + "version": "1.5.0", + "resolved": "git+https://git.squeep.com/squeep-authentication-module/#5ea2ffe571a74618eef073c58c5fef06e1cf06a7", "dependencies": { - "@squeep/api-dingus": "^2.1.0", - "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0", - "@squeep/indieauth-helper": "^1.4.1", - "@squeep/mystery-box": "^2.0.2", - "@squeep/totp": "^1.1.4" + "@squeep/api-dingus": "^2", + "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.1", + "@squeep/indieauth-helper": "^1", + "@squeep/mystery-box": "^2", + "@squeep/totp": "^1", + "uuid": "^9" }, "engines": { - "node": "^18" + "node": ">=18" }, "optionalDependencies": { "argon2": "^0.40.1", @@ -968,94 +1020,88 @@ "node_modules/@squeep/chores": { "version": "1.0.1", "resolved": "git+https://git.squeep.com/squeep-chores/#a77e8814cbba0ad751e249850d1f7b144da6446b", - "license": "ISC", "dependencies": { "@squeep/log-helper": "^1.0.0" } }, + "node_modules/@squeep/eslint-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@squeep/eslint-config/-/eslint-config-1.0.0.tgz", + "integrity": "sha512-kGuzqWqQekblecm4egJvY/2Wa0wxEDUlFPxwZ4gu8Eglmf9m/2rs2rr85DvtTxt43fvNcZ7IDuspTOv+Ei9BSg==", + "dev": true, + "dependencies": { + "@eslint/js": "^9", + "eslint-plugin-jsdoc": "^48", + "eslint-plugin-n": "^17", + "eslint-plugin-security": "^3", + "eslint-plugin-sonarjs": "^1", + "globals": "^15" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "eslint": ">= 9" + } + }, "node_modules/@squeep/html-template-helper": { - "version": "1.6.0", - "resolved": "git+https://git.squeep.com/squeep-html-template-helper#2d0ba72a2ea35f45c1ab1ac81fce3d0cbe7db419", - "license": "ISC", + "version": "1.6.1", + "resolved": "git+https://git.squeep.com/squeep-html-template-helper#93d1b030d6b3c6ea93c36a46f4940181a1acaca0", "dependencies": { - "@squeep/lazy-property": "^1.1.2" + "@squeep/lazy-property": "^1" }, "engines": { - "node": ">=14" + "node": ">=14.13.1" } }, "node_modules/@squeep/indieauth-helper": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@squeep/indieauth-helper/-/indieauth-helper-1.4.1.tgz", - "integrity": "sha512-x/yqrjrbp0vTdvIW8e9CKsr5+YwmDp/x4nK4H5LInt07rk4nthxv7e7qIgy97OFtvFnoNee+N9gyi3TzIIiGoQ==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@squeep/indieauth-helper/-/indieauth-helper-1.4.2.tgz", + "integrity": "sha512-PKOr2Mx8e1RwdJ6ab22gWAlbzktDNORIt4xzGZ0iXbvbTdqNLdpAKBHi8Il0vYgZU0HAO3utNqMX5grSfKaDrw==", "dependencies": { - "@squeep/log-helper": "^1.0.0", - "@squeep/web-linking": "^1.0.8", - "got": "^13.0.0", - "iconv": "^3.0.1", - "ip-address": "^9.0.5", - "microformats-parser": "^2.0.2" + "@squeep/log-helper": "^1", + "@squeep/web-linking": "^1", + "got": "^14", + "iconv": "^3", + "ip-address": "^9", + "microformats-parser": "^2" }, "engines": { - "node": "^14 >=14.18 || >=15.7" + "node": ">=20" } }, "node_modules/@squeep/lazy-property": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@squeep/lazy-property/-/lazy-property-1.1.2.tgz", - "integrity": "sha512-wRdR4IOqWXoDMArx0HPo5MtM2Wk5wemAULbZ6PabVw1ylSQekkzKfoAUuupxsKuzjcRPjZvbpGDv+i04hBMnQw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@squeep/lazy-property/-/lazy-property-1.1.3.tgz", + "integrity": "sha512-GzQ/bSE6MZ6YeRjLMKK18I0ajvOnhrgacZs6EpLERXPPlQZWkOcmjCcjyqY71lqd5MzsPbSwzsj6Uonpqea6nw==", "engines": { "node": ">=14" } }, "node_modules/@squeep/log-helper": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@squeep/log-helper/-/log-helper-1.0.0.tgz", - "integrity": "sha512-i61ECZLWQI2rhkXj9pDzH1Md5ICghL9zvh5QFVo0BTayuSrdS9SWkJ6gV1qWki/Xz6SuE0y0y145NyHlvOuVaw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@squeep/log-helper/-/log-helper-1.0.1.tgz", + "integrity": "sha512-T+QTxSNoZ9wzyyK1jS8ac9puOikRv14MfkE3uXSBOp70T8eEqCjSwfE+VHtOb0riU4FhYpDsBNSMIskK4UDRZA==", "engines": { - "node": ">=14" + "node": ">=14.13.1" } }, "node_modules/@squeep/logger-json-console": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@squeep/logger-json-console/-/logger-json-console-3.0.2.tgz", - "integrity": "sha512-Qz2QMwhyyRB5sZFB/S6eUt7TnmKCPB6oUqa8SW2gGGOLTcvf+0nbFgqqzlFwtUEwF3KwCmMnOt7lwq5PLrm24Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@squeep/logger-json-console/-/logger-json-console-3.0.3.tgz", + "integrity": "sha512-W8BDrV0fHKABbFXOedJKd8hmIgKOtTCM0lLGmaGsDfV8+OAdEg4kdS0F8GI7PLF9mOzp/z0uRv+CCyv5tS81kA==", "engines": { "node": ">=17" } }, "node_modules/@squeep/mystery-box": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@squeep/mystery-box/-/mystery-box-2.0.2.tgz", - "integrity": "sha512-YoVx9F/ZFOdgPrt5ey3Vg+ttK4nsnfeSjzVDBOCB1L5q2H1V2wZ4DV0/l7mkqPgHHojGeJqncGs/KhB6Lu916g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@squeep/mystery-box/-/mystery-box-2.0.3.tgz", + "integrity": "sha512-OFPVVyYrxZU/8iwJGBRpyh+z9AyEqryWyh8p45LtaHw62hwOb8k2J8An4jFmYU0fiMRR+b3xR94qt0/V+ansyA==", "engines": { "node": "^14 >=14.18.0 || >=15.7.0" } }, - "node_modules/@squeep/resource-authentication-module": { - "version": "1.0.1", - "resolved": "git+https://git.squeep.com/squeep-resource-authentication-module#0dce6b122b90dc9d2fd3b7191cdef4d41e256ae3", - "license": "ISC", - "dependencies": { - "@squeep/api-dingus": "v2.0.0", - "uuid": "^9.0.0" - }, - "engines": { - "node": "^14 >=14.18 || >=15.7" - } - }, - "node_modules/@squeep/resource-authentication-module/node_modules/@squeep/api-dingus": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@squeep/api-dingus/-/api-dingus-2.0.0.tgz", - "integrity": "sha512-HKz/yB1KNmEcHB92KIvrNvwMph5fSdJBrxKSgERYfyQkLFl2vSwDV+IlFvi68DYmMBP3lWKzQcTXWBMYlW3c0g==", - "dependencies": { - "mime-db": "^1.52.0", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@squeep/roman": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@squeep/roman/-/roman-1.0.1.tgz", @@ -1065,7 +1111,6 @@ "version": "1.0.1", "resolved": "git+https://git.squeep.com/squeep-test-helper#cc0f69b40de9ae3342f1b7a1784d37769e7f1e84", "dev": true, - "license": "ISC", "dependencies": { "eslint": "^8.53.0", "eslint-plugin-node": "^11.1.0", @@ -1077,6 +1122,107 @@ "sinon": "^17.0.1" } }, + "node_modules/@squeep/test-helper/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@squeep/test-helper/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@squeep/test-helper/node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@squeep/test-helper/node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@squeep/test-helper/node_modules/eslint-plugin-security": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz", @@ -1098,10 +1244,108 @@ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@squeep/test-helper/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@squeep/test-helper/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@squeep/test-helper/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@squeep/test-helper/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@squeep/test-helper/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@squeep/test-helper/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@squeep/test-helper/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@squeep/totp": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@squeep/totp/-/totp-1.1.4.tgz", - "integrity": "sha512-cMoicNB5xIDMdcOtTfkzWZ0eQCepatTsFoWXtQ8Ja4FfvAA3ZWwIMfKV4K7zbx1MjYGF/Ufikxa6CaPS6yd5mw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@squeep/totp/-/totp-1.1.5.tgz", + "integrity": "sha512-keqggH2NrHs8hqzyov31zIA4XTLUxwXBn+VfUFlCdzZY2omoWbgm4742Ht8j3W48FLtIX1q4Zrm1ncObi9RfMA==", "dependencies": { "base32.js": "^0.1.0", "qrcode-svg": "^1.1.0" @@ -1111,9 +1355,9 @@ } }, "node_modules/@squeep/web-linking": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@squeep/web-linking/-/web-linking-1.0.8.tgz", - "integrity": "sha512-+0nCl/IXY8RHBWNr5mJMkU+U62in9xYDigIMkMKBp0bWnah/gjXv3NxAa4+LkxCMZU0STt/uBjuVM7SpvuQHSg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@squeep/web-linking/-/web-linking-1.0.9.tgz", + "integrity": "sha512-NHI2sWiL7PduRBYhM54MqG40xnI867uRyjKDWBCudsel4DmLy3Jh1CIvYORbPt8ScvvL0PV7kCsmowPdufAOkA==", "engines": { "node": ">=14.0" } @@ -1129,11 +1373,46 @@ "node": ">=14.16" } }, + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1209,9 +1488,9 @@ } }, "node_modules/amqplib": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", - "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.4.tgz", + "integrity": "sha512-DMZ4eCEjAVdX1II2TfIUpJhfKAuoCeDIo/YyETbfAqehHTXxxs7WOOd+N1Xxr4cKhx12y23zk8/os98FxlZHrw==", "dependencies": { "@acuminous/bitsyntax": "^0.1.2", "buffer-more-ints": "~1.0.0", @@ -1292,6 +1571,15 @@ "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/are-we-there-yet": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", @@ -1395,9 +1683,9 @@ ] }, "node_modules/better-sqlite3": { - "version": "9.4.5", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.4.5.tgz", - "integrity": "sha512-uFVyoyZR9BNcjSca+cp3MWCv6upAv+tbMC4SWM51NIMhoQOm4tjIkyxFO/ZsYdGAF61WJBgdzyJcz4OokJi0gQ==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz", + "integrity": "sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==", "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", @@ -1575,18 +1863,9 @@ "dev": true, "engines": { "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cacheable-lookup": { @@ -1614,6 +1893,17 @@ "node": ">=14.16" } }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1648,9 +1938,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001606", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001606.tgz", - "integrity": "sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg==", + "version": "1.0.30001617", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", + "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", "dev": true, "funding": [ { @@ -1723,13 +2013,9 @@ } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "optional": true, - "engines": { - "node": ">=10" - } + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "node_modules/clean-stack": { "version": "2.2.0", @@ -1815,6 +2101,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "devOptional": true }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -2006,15 +2301,11 @@ "optional": true }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/diff": { @@ -2045,9 +2336,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.729", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz", - "integrity": "sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==", + "version": "1.4.763", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.763.tgz", + "integrity": "sha512-k4J8NrtJ9QrvHLRo8Q18OncqBCB7tIUyqxRcJnlonQ0ioHKYB988GcDFF3ZePmnb8eHEopDs/wPHR/iGAFgoUQ==", "dev": true }, "node_modules/emoji-regex": { @@ -2064,6 +2355,19 @@ "once": "^1.4.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -2103,41 +2407,37 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.2.0.tgz", + "integrity": "sha512-0n/I88vZpCOzO+PQpt0lbsqmn9AsnsJAQseIqhZFI8ibQT0U1AkEKRxA3EVMos0BoHSXDQvCXY25TUjB5tr8Og==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/eslintrc": "^3.0.2", + "@eslint/js": "9.2.0", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.2.3", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -2151,7 +2451,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2211,32 +2511,76 @@ "eslint": ">=8" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "48.2.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.4.tgz", + "integrity": "sha512-3ebvVgCJFy06gpmuS2ynz13uh9iFSzZ1C1dDkgcSAqVVg82zlORKMk2fvjq708pAO6bwfs5YLttknFEbaoDiGw==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.43.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.6.0.tgz", + "integrity": "sha512-Y73o88ROwbCtVCCmZjYlYcPYkOG7mIzxxVK1XFRSa2epbKWtAPsmYpAD0pqxg/ZwlcWxMDceQPKHYQi4VIHz7w==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", + "enhanced-resolve": "^5.15.0", "eslint-plugin-es-x": "^7.5.0", "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", + "globals": "^15.0.0", "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", + "minimatch": "^9.0.0", "semver": "^7.5.3" }, "engines": { - "node": ">=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "eslint": ">=7.0.0" + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/eslint-plugin-node": { @@ -2268,50 +2612,41 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/eslint-plugin-security": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-2.1.1.tgz", - "integrity": "sha512-7cspIGj7WTfR3EhaILzAPcfCo5R9FbeWvbgsPYWivSurTBKW88VQxtP3c4aWMG9Hz/GfJlJVdXEJ3c8LqS+u2w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-3.0.0.tgz", + "integrity": "sha512-2Ij7PkmXIF2cKwoVkEgemwoXbOnxg5UfdhdcpNxZwJxC/10dbsdhHISrTyJ/n8DUkt3yiN6P1ywEgcMGjIwHIw==", "dev": true, "dependencies": { "safe-regex": "^2.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/eslint-plugin-sonarjs": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.1.tgz", - "integrity": "sha512-5IOKvj/GMBNqjxBdItfotfRHo7w48496GOu1hxdeXuD0mB1JBlDCViiLHETDTfA8pDAVSBimBEQoetRXYceQEw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-1.0.3.tgz", + "integrity": "sha512-6s41HLPYPyDrp+5+7Db5yFYbod6h9pC7yx+xfcNwHRcLe1EZwbbQT/tdOAkR7ekVUkNGEvN3GmYakIoQUX7dEg==", "dev": true, "engines": { "node": ">=16" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.0.0 || ^9.0.0" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2342,29 +2677,29 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.11.3", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2461,15 +2796,15 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-uri-to-path": { @@ -2532,17 +2867,16 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -2568,11 +2902,11 @@ } }, "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", + "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==", "engines": { - "node": ">= 14.17" + "node": ">= 18" } }, "node_modules/fromentries": { @@ -2756,20 +3090,20 @@ } }, "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -2784,22 +3118,22 @@ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.3.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", + "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.6", "minimatch": "^9.0.1", "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "path-scurry": "^1.11.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2842,39 +3176,36 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.2.0.tgz", + "integrity": "sha512-FQ5YwCHZM3nCmtb5FzEWwdUc9K5d3V/w9mzcz8iGD1gC/aOTHc6PouYu0kkKipNJqHAT7m51sqzQjEjIP+cK0A==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/got": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", - "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/got/-/got-14.2.1.tgz", + "integrity": "sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==", "dependencies": { - "@sindresorhus/is": "^5.2.0", + "@sindresorhus/is": "^6.1.0", "@szmarczak/http-timer": "^5.0.1", "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", + "cacheable-request": "^10.2.14", "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", + "form-data-encoder": "^4.0.2", + "get-stream": "^8.0.1", + "http2-wrapper": "^2.2.1", "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", + "p-cancelable": "^4.0.1", "responselike": "^3.0.0" }, "engines": { - "node": ">=16" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/got?sponsor=1" @@ -2923,15 +3254,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2960,9 +3282,9 @@ "dev": true }, "node_modules/html-validate": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-8.18.1.tgz", - "integrity": "sha512-6NYRciFBQhVZH29fwDQxofPil0qm7MMSEDzDpZWu2U23Fnmv1WTuompP7fbzHYj6+CIJ4T/Q4hyWRCe0CCrsMg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-8.18.2.tgz", + "integrity": "sha512-X/ahjDnTZe1VjoPz+w7zGe5I5/os8PLQ+c5C9ak6etV0nNK9gPbwbGaP9BQoSQuQx0mszw5khDjP1ffnXx+1OA==", "dev": true, "funding": [ { @@ -2970,6 +3292,10 @@ "url": "https://github.com/sponsors/html-validate" } ], + "workspaces": [ + "docs", + "tests/vitest" + ], "dependencies": { "@babel/code-frame": "^7.10.0", "@html-validate/stylish": "^4.1.0", @@ -3011,15 +3337,15 @@ } }, "node_modules/html-validate/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -3479,6 +3805,15 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -3700,9 +4035,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -3888,9 +4223,9 @@ } }, "node_modules/node-abi": { - "version": "3.57.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz", - "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==", + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", "dependencies": { "semver": "^7.3.5" }, @@ -3928,9 +4263,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -4421,17 +4756,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -4447,11 +4782,11 @@ } }, "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", "engines": { - "node": ">=12.20" + "node": ">=14.16" } }, "node_modules/p-limit": { @@ -4577,34 +4912,34 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, "engines": { "node": "14 || >=16.14" } }, "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true }, "node_modules/pg": { @@ -4924,14 +5259,6 @@ "node": ">=10" } }, - "node_modules/prebuild-install/node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5302,12 +5629,9 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -5315,22 +5639,6 @@ "node": ">=10" } }, - "node_modules/semver/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==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -5423,17 +5731,17 @@ } }, "node_modules/sinon": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", - "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.2.tgz", + "integrity": "sha512-uihLiaB9FhzesElPDFZA7hDcNABzsVHwr3YfmM9sBllVwab3l0ltGlRV1XhpNfIacNDLGD1QRZNLs5nU5+hTuA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^3.0.0", + "@sinonjs/commons": "^3.0.1", "@sinonjs/fake-timers": "^11.2.2", "@sinonjs/samsam": "^8.0.0", - "diff": "^5.1.0", - "nise": "^5.1.5", - "supports-color": "^7.2.0" + "diff": "^5.2.0", + "nise": "^5.1.9", + "supports-color": "^7" }, "funding": { "type": "opencollective", @@ -5511,6 +5819,28 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, "node_modules/spex": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/spex/-/spex-3.3.0.tgz", @@ -5678,6 +6008,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -5706,11 +6045,6 @@ "tar-stream": "^2.1.4" } }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -5766,6 +6100,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/tar/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "engines": { + "node": ">=10" + } + }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -5881,15 +6224,12 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/typedarray": { @@ -5908,9 +6248,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", + "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", "dev": true, "funding": [ { @@ -5927,7 +6267,7 @@ } ], "dependencies": { - "escalade": "^3.1.1", + "escalade": "^3.1.2", "picocolors": "^1.0.0" }, "bin": { @@ -6038,6 +6378,15 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/package.json b/package.json index b0f36f3..85a53ed 100644 --- a/package.json +++ b/package.json @@ -12,18 +12,20 @@ ], "main": "server.js", "scripts": { + "audit": "npm audit", "coverage": "nyc npm test", "coverage-check": "nyc check-coverage", "eslint": "eslint server.js src", "test": "mocha --recursive" }, "pre-commit": [ + "audit", "eslint", "coverage", "coverage-check" ], "engines": { - "node": "^14 >=14.18 || >=15.7" + "node": ">=16.9" }, "repository": { "type": "git", @@ -33,32 +35,28 @@ "license": "ISC", "dependencies": { "@squeep/amqp-helper": "git+https://git.squeep.com/squeep-amqp-helper#v1.0.0", - "@squeep/api-dingus": "^2.1.0", - "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.4.0", + "@squeep/api-dingus": "^2", + "@squeep/authentication-module": "git+https://git.squeep.com/squeep-authentication-module/#v1.5.0", "@squeep/chores": "git+https://git.squeep.com/squeep-chores/#v1.0.1", - "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.0", - "@squeep/indieauth-helper": "^1.4.1", - "@squeep/logger-json-console": "^3.0.2", - "@squeep/mystery-box": "^2.0.2", - "@squeep/resource-authentication-module": "git+https://git.squeep.com/squeep-resource-authentication-module#v1.0.1", - "@squeep/roman": "^1.0.1", - "@squeep/web-linking": "^1.0.8", - "better-sqlite3": "^9.4.5", - "pg-promise": "^11.6.0", - "uuid": "^9.0.1" + "@squeep/html-template-helper": "git+https://git.squeep.com/squeep-html-template-helper#v1.6.1", + "@squeep/indieauth-helper": "^1.4", + "@squeep/logger-json-console": "^3", + "@squeep/mystery-box": "^2", + "@squeep/roman": "^1", + "@squeep/web-linking": "^1", + "better-sqlite3": "^9", + "pg-promise": "^11", + "uuid": "^9" }, "devDependencies": { + "@squeep/eslint-config": "^1", "@squeep/test-helper": "git+https://git.squeep.com/squeep-test-helper#v1.0.1", - "eslint": "^8.57.0", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-security": "^2.1.1", - "eslint-plugin-sonarjs": "^0.25.1", - "html-validate": "^8.18.1", - "mocha": "^10.4.0", - "mocha-steps": "^1.3.0", - "nyc": "^15.1.0", - "pre-commit": "^1.2.2", - "sinon": "^17.0.1" + "eslint": "^9", + "html-validate": "^8", + "mocha": "^10", + "mocha-steps": "^1", + "nyc": "^15", + "pre-commit": "^1", + "sinon": "^17" } } diff --git a/src/chores.js b/src/chores.js index 4002158..bd21f6f 100644 --- a/src/chores.js +++ b/src/chores.js @@ -23,7 +23,7 @@ class Chores extends BaseChores { /** * Attempt to remove tokens which are expired or otherwise no longer valid. - * @param {Number} atLeastMsSinceLast + * @param {number} atLeastMsSinceLast minimum clean period */ async cleanTokens(atLeastMsSinceLast = this.options?.chores?.tokenCleanupMs || 0) { const _scope = _fileScope('cleanTokens'); @@ -47,7 +47,7 @@ class Chores extends BaseChores { /** * Attempt to remove ephemeral scopes which are no longer referenced by tokens. - * @param {Number} atLeastMsSinceLast + * @param {number} atLeastMsSinceLast minimum clean period */ async cleanScopes(atLeastMsSinceLast = this.options?.chores?.scopeCleanupMs || 0) { const _scope = _fileScope('cleanScopes'); diff --git a/src/common.js b/src/common.js index a9427c2..0855720 100644 --- a/src/common.js +++ b/src/common.js @@ -8,9 +8,9 @@ const randomBytesAsync = promisify(randomBytes); /** * Limit length of string to keep logs sane - * @param {String} str - * @param {Number} len - * @returns {String} + * @param {string} str str + * @param {number} len len + * @returns {string} str */ const logTruncate = (str, len) => { if (typeof str !== 'string' || str.toString().length <= len) { @@ -21,9 +21,9 @@ const logTruncate = (str, len) => { /** * Turn a snake into a camel. - * @param {String} snakeCase - * @param {String|RegExp} delimiter - * @returns {String} + * @param {string} snakeCase snake case + * @param {string | RegExp} delimiter delimiter + * @returns {string} camel case */ const camelfy = (snakeCase, delimiter = '_') => { if (!snakeCase || typeof snakeCase.split !== 'function') { @@ -38,7 +38,8 @@ const camelfy = (snakeCase, delimiter = '_') => { /** * Return an array containing x if x is not an array. - * @param {*} x + * @param {*} x x + * @returns {any[]} x[] */ const ensureArray = (x) => { if (x === undefined) { @@ -52,8 +53,8 @@ const ensureArray = (x) => { /** * Recursively freeze an object. - * @param {Object} o - * @returns {Object} + * @param {object} o obj + * @returns {object} frozen obj */ const freezeDeep = (o) => { Object.freeze(o); @@ -68,9 +69,13 @@ const freezeDeep = (o) => { }; -/** Oauth2.1 §3.2.3.1 +/** + * Oauth2.1 §3.2.3.1 * %x20-21 / %x23-5B / %x5D-7E - * @param {String} char + * ' '-'!' / '#'-'[' / ']'-'~' + * not allowed: control characters, '"', '\' + * @param {string} char character + * @returns {boolean} is valid */ const validErrorChar = (char) => { const value = char.charCodeAt(0); @@ -82,8 +87,8 @@ const validErrorChar = (char) => { /** * Determine if an OAuth error message is valid. - * @param {String} error - * @returns {Boolean} + * @param {string} error error + * @returns {boolean} is valid */ const validError = (error) => { return error && error.split('').filter((c) => !validErrorChar(c)).length === 0 || false; @@ -93,7 +98,8 @@ const validError = (error) => { /** * OAuth2.1 §3.2.2.1 * scope-token = 1*( %x21 / %x23-5B / %x5D-7E ) - * @param {String} char + * @param {string} char char + * @returns {boolean} is valid */ const validScopeChar = (char) => { const value = char.charCodeAt(0); @@ -105,8 +111,8 @@ const validScopeChar = (char) => { /** * Determine if a scope has a valid name. - * @param {String} scope - * @returns {Boolean} + * @param {string} scope scope + * @returns {boolean} is valid */ const validScope = (scope) => { return scope && scope.split('').filter((c) => !validScopeChar(c)).length === 0 || false; @@ -115,7 +121,8 @@ const validScope = (scope) => { /** * - * @param {Number} bytes + * @param {number} bytes bytes + * @returns {string} base64 random string */ const newSecret = async (bytes = 64) => { return (await randomBytesAsync(bytes * 3 / 4)).toString('base64'); @@ -124,8 +131,8 @@ const newSecret = async (bytes = 64) => { /** * Convert a Date object to epoch seconds. - * @param {Date=} date - * @returns {Number} + * @param {Date=} date date + * @returns {number} epoch */ const dateToEpoch = (date) => { const dateMs = date ? date.getTime() : Date.now(); @@ -138,10 +145,16 @@ const omit = (o, props) => { }; +/** + * @typedef {object} ConsoleLike + * @property {Function} debug log debug + */ + /** * Log Mystery Box statistics events. - * @param {Console} logger - * @param {String} scope + * @param {ConsoleLike} logger logger instance + * @param {string} scope scope + * @returns {Function} stat logger */ const mysteryBoxLogger = (logger, scope) => { return (s) => { diff --git a/src/db/abstract.js b/src/db/abstract.js index 953034d..a127c39 100644 --- a/src/db/abstract.js +++ b/src/db/abstract.js @@ -21,7 +21,7 @@ class Database { * At the minimum, this will validate a compatible schema is present and usable. * Some engines will also perform other initializations or async actions which * are easier handled outside the constructor. - */ + */ async initialize() { const _scope = _fileScope('initialize'); @@ -39,15 +39,15 @@ class Database { /** - * @typedef {Object} SchemaVersionObject - * @property {Number} major - * @property {Number} minor - * @property {Number} patch + * @typedef {object} SchemaVersionObject + * @property {number} major major + * @property {number} minor minor + * @property {number} patch patch */ /** * Query the current schema version. * This is a standalone query function, as it is called before statements are loaded. - * @returns {Promise} + * @returns {Promise} schema version */ async _currentSchema() { this._notImplemented('_currentSchema', arguments); @@ -73,7 +73,7 @@ class Database { /** * Wrap a function call in a transaction context. - * @param {*} dbCtx + * @param {*} dbCtx db context * @param {Function} fn fn(txCtx) */ async transaction(dbCtx, fn) { @@ -101,9 +101,9 @@ class Database { * - infinites * - null * - uuid - * @param {Object} object - * @param {String[]} properties - * @param {String[]} types + * @param {object} object object + * @param {string[]} properties properties + * @param {string[]} types types */ _ensureTypes(object, properties, types) { const _scope = _fileScope('_ensureTypes'); @@ -157,14 +157,14 @@ class Database { /** - * @typedef {Object} Authentication - * @property {String} identifier - * @property {String=} credential - * @property {Date} created - * @property {Date=} lastAuthentication + * @typedef {object} Authentication + * @property {string} identifier identifier + * @property {string=} credential credential + * @property {Date} created created + * @property {Date=} lastAuthentication last authentication */ /** - * @param {Authentication} authentication + * @param {Authentication} authentication authentication */ _validateAuthentication(authentication) { [ @@ -177,14 +177,14 @@ class Database { /** - * @typedef {Object} Resource - * @property {String} resourceId - uuid - * @property {String} secret - * @property {String} description - * @property {Date} created + * @typedef {object} Resource + * @property {string} resourceId uuid + * @property {string} secret secret + * @property {string} description description + * @property {Date} created created at */ /** - * @param {Resource} resource + * @param {Resource} resource resource */ _validateResource(resource) { [ @@ -196,24 +196,24 @@ class Database { /** - * @typedef {Object} Token - * @property {String} codeId - uuid - * @property {String} profile - * @property {Date} created - * @property {Date=} expires - * @property {Date=} refreshExpires - * @property {Date=} refreshed - * @property {*=} duration - * @property {*=} refreshDuration - * @property {Number|BigInt=} refresh_count - * @property {Boolean} is_revoked - * @property {Boolean} is_token - * @property {String} client_id - * @property {String[]} scopes - * @property {Object=} profileData + * @typedef {object} Token + * @property {string} codeId uuid + * @property {string} profile profile + * @property {Date} created created at + * @property {Date=} expires expires at + * @property {Date=} refreshExpires refresh expires at + * @property {Date=} refreshed refreshed at + * @property {*=} duration duration + * @property {*=} refreshDuration refresh duration + * @property {number | bigint=} refresh_count refresh count + * @property {boolean} is_revoked is revoked + * @property {boolean} is_token is token + * @property {string} client_id client id + * @property {string[]} scopes scopes + * @property {object=} profileData profile data */ /** - * @param {Token} token + * @param {Token} token token */ _validateToken(token) { [ @@ -233,8 +233,8 @@ class Database { * Interface methods need implementations. Ensure the db-interaction * methods on the base class call this, so they may be overridden by * implementation classes. - * @param {String} method - * @param {arguments} args + * @param {string} method method + * @param {arguments} args args */ _notImplemented(method, args) { this.logger.error(_fileScope(method), 'abstract method called', Array.from(args)); @@ -244,7 +244,7 @@ class Database { /** * Get all the almanac entries. - * @param {*} dbCtx + * @param {*} dbCtx db context */ async almanacGetAll(dbCtx) { this._notImplemented('almanacGetAll', arguments); @@ -253,9 +253,9 @@ class Database { /** * Insert or update an almanac entry. - * @param {*} dbCtx - * @param {String} event - * @param {Date=} date + * @param {*} dbCtx db context + * @param {string} event event + * @param {Date=} date date */ async almanacUpsert(dbCtx, event, date) { this._notImplemented('almanacUpsert', arguments); @@ -264,9 +264,9 @@ class Database { /** * Fetch the authentication record for an identifier. - * @param {*} dbCtx - * @param {String} identifier - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} identifier identifier + * @returns {Promise} authentication */ async authenticationGet(dbCtx, identifier) { this._notImplemented('authenticationGet', arguments); @@ -276,8 +276,8 @@ class Database { /** * Update the authentication record for the identifier that * correct credentials have been supplied. - * @param {*} dbCtx - * @param {String} identifier + * @param {*} dbCtx db context + * @param {string} identifier identifier * @returns {Promise} */ async authenticationSuccess(dbCtx, identifier) { @@ -287,10 +287,10 @@ class Database { /** * Insert or update the credential for an identifier. - * @param {*} dbCtx - * @param {String} identifier - * @param {String} credential - * @param {String=} otpKey + * @param {*} dbCtx db context + * @param {string} identifier identifier + * @param {string} credential credential + * @param {string=} otpKey otp key * @returns {Promise} */ async authenticationUpsert(dbCtx, identifier, credential, otpKey) { @@ -300,9 +300,9 @@ class Database { /** * Update the otpKey for an identifier. - * @param {*} dbCtx - * @param {String} identifier - * @param {String=} otpKey + * @param {*} dbCtx db context + * @param {string} identifier identifier + * @param {string=} otpKey otp key * @returns {Promise} */ async authenticationUpdateOTPKey(dbCtx, identifier, otpKey) { @@ -312,9 +312,9 @@ class Database { /** * Update the credential for an identifier. - * @param {*} dbCtx - * @param {String} identifier - * @param {String} credential + * @param {*} dbCtx db context + * @param {string} identifier identifier + * @param {string} credential credential * @returns {Promise} */ async authenticationUpdateCredential(dbCtx, identifier, credential) { @@ -324,9 +324,9 @@ class Database { /** * Determine if profile url is known to this service. - * @param {*} dbCtx - * @param {String} profile - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} profile profile + * @returns {Promise} is valid */ async profileIsValid(dbCtx, profile) { this._notImplemented('profileGet', arguments); @@ -336,9 +336,9 @@ class Database { /** * Insert a new relationship between a profile endpoint and * an authenticated identifier. - * @param {*} dbCtx - * @param {String} profile - * @param {String} identifier + * @param {*} dbCtx db context + * @param {string} profile profile + * @param {string} identifier identifier * @returns {Promise} */ async profileIdentifierInsert(dbCtx, profile, identifier) { @@ -348,9 +348,9 @@ class Database { /** * Adds a scope to be available for a profile to include on any authorization request. - * @param {*} dbCtx - * @param {String} profile - * @param {String} scope + * @param {*} dbCtx db context + * @param {string} profile profile + * @param {string} scope scope * @returns {Promise} */ async profileScopeInsert(dbCtx, profile, scope) { @@ -359,24 +359,26 @@ class Database { /** - * @typedef {Object} ScopeDetails - * @property {String} description - * @property {String[]=} profiles + * @typedef {object} ScopeDetails + * @property {string} description description + * @property {string[]=} profiles profiles + */ + /** + * @typedef {object} Profile + * @property {ScopeDetails} scope scope */ /** - * @typedef {Object.} ProfileScopes - * @property {Object.} profile - * @property {Object.} profile.scope + * @typedef {{[profile: string]: Profile}} ProfileScopes */ /** - * @typedef {Object.} ScopeIndex - * @property {ScopeDetails} scope + * @typedef {{[scope: string]: ScopeDetails}} ScopeIndex + * @property {ScopeDetails} scope scope details */ /** - * @typedef {Object} ProfilesScopesReturn - * @property {ProfileScopes} profileScopes - * @property {ScopeIndex} scopeIndex - * @property {String[]} profiles + * @typedef {object} ProfilesScopesReturn + * @property {ProfileScopes} profileScopes profile scopes + * @property {ScopeIndex} scopeIndex scope index + * @property {string[]} profiles profiles */ /** * Returns an object containing: @@ -384,9 +386,9 @@ class Database { * which each contain a description of the scope and a list of profiles offering it * - an object with scopes as keys to the same scope objects * - a list of profiles - * @param {*} dbCtx - * @param {String} identifier - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} identifier identifier + * @returns {Promise} profiles scopes */ async profilesScopesByIdentifier(dbCtx, identifier) { this._notImplemented('profilesScopesByIdentifier', arguments); @@ -395,18 +397,18 @@ class Database { /** * @typedef ProfileScopesRow - * @property profile - * @property scope - * @property description - * @property application - * @property isPermanent - * @property isManuallyAdded + * @property {string} profile profile + * @property {string} scope scope + * @property {string} description description + * @property {string} application application + * @property {boolean} isPermanent avoid cleanup + * @property {boolean} isManuallyAdded avoid cleanup */ /** * Convert db row data into associative structures. * Same behavior is shared by multiple engines. - * @param {ProfileScopesRow[]} profileScopesRows - * @returns {ProfileScopesReturn} + * @param {ProfileScopesRow[]} profileScopesRows profile scopes row + * @returns {ProfilesScopesReturn} profiles scopes */ static _profilesScopesBuilder(profileScopesRows) { const scopeIndex = {}; @@ -445,9 +447,9 @@ class Database { /** * Sets list of additional scopes available to profile. - * @param {*} dbCtx - * @param {String} profile - * @param {String[]} scopes + * @param {*} dbCtx db context + * @param {string} profile profile + * @param {string[]} scopes scopes * @returns {Promise} */ async profileScopesSetAll(dbCtx, profile, scopes) { @@ -457,20 +459,19 @@ class Database { /** * Create (or revoke a duplicate) code as a token entry. - * @param {*} dbCtx - * @param {Object} data - * @param {String} data.codeId - * @param {Date} data.created - * @param {Boolean} data.isToken - * @param {String} data.clientId - * @param {String} data.profile - profile uri - * @param {String} data.identifier - * @param {String[]} data.scopes - * @param {Number|Null} data.lifespanSeconds - null sets expiration to Infinity - * @param {Number|Null} data.refreshLifespanSeconds - null sets refresh to none - * @param {String|Null} data.resource - * @param {Object|Null} data.profileData - profile data from profile uri - * @returns {Promise} whether redemption was successful + * @param {*} dbCtx db context + * @param {object} data data + * @param {string} data.codeId code id + * @param {Date} data.created created at + * @param {boolean} data.isToken is token + * @param {string} data.clientId client id + * @param {string} data.profile profile uri + * @param {string} data.identifier identifier + * @param {string[]} data.scopes scopesx + * @param {number | null} data.lifespanSeconds null sets expiration to Infinity + * @param {number | null} data.refreshLifespanSeconds null sets refresh to none + * @param {object | null} data.profileData profile data from profile uri + * @returns {Promise} whether redemption was successful */ async redeemCode(dbCtx, { codeId, created, isToken, clientId, profile, identifier, scopes, lifespanSeconds, refreshLifespanSeconds, profileData } = {}) { this._notImplemented('redeemCode', arguments); @@ -478,18 +479,18 @@ class Database { /** - * @typedef {Object} RefreshedToken - * @property {Date} expires - * @property {Date} refreshExpires - * @property {String[]=} scopes if scopes were reduced + * @typedef {object} RefreshedToken + * @property {Date} expires expires at + * @property {Date} refreshExpires refresh expires at + * @property {string[]=} scopes if scopes were reduced */ /** * Redeem a refresh token to renew token codeId. - * @param {*} dbCtx - * @param {String} codeId - * @param {Date} refreshed - * @param {String[]} removeScopes - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} codeId code id + * @param {Date} refreshed refreshed at + * @param {string[]} removeScopes remove scopes + * @returns {Promise} refreshed token */ async refreshCode(dbCtx, codeId, refreshed, removeScopes) { this._notImplemented('refreshCode', arguments); @@ -498,9 +499,9 @@ class Database { /** * Fetch a resource server record. - * @param {*} dbCtx - * @param {String} identifier uuid - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} resourceId uuid + * @returns {Promise} resource */ async resourceGet(dbCtx, resourceId) { this._notImplemented('resourceGet', arguments); @@ -509,10 +510,10 @@ class Database { /** * Create, or update description of, a resourceId. - * @param {*} dbCtx - * @param {String=} resourceId uuid - * @param {String=} secret - * @param {String=} description + * @param {*} dbCtx db context + * @param {string=} resourceId uuid + * @param {string=} secret secret + * @param {string=} description description * @returns {Promise} */ async resourceUpsert(dbCtx, resourceId, secret, description) { @@ -522,10 +523,11 @@ class Database { /** * Register a scope and its description. - * @param {*} dbCtx - * @param {String} scope - * @param {String} application - * @param {String} description + * @param {*} dbCtx db context + * @param {string} scope scope + * @param {string} application application + * @param {string} description description + * @param {boolean} manuallyAdded is manually added * @returns {Promise} */ async scopeUpsert(dbCtx, scope, application, description, manuallyAdded = false) { @@ -535,9 +537,9 @@ class Database { /** * Remove a non-permanent scope if it is not currently in use. - * @param {*} dbCtx - * @param {String} scope - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} scope scope + * @returns {Promise} deleted */ async scopeDelete(dbCtx, scope) { this._notImplemented('scopeDelete', arguments); @@ -545,16 +547,16 @@ class Database { /** - * @typedef {Number|BigInt} CleanupResult + * @typedef {number | bigint} CleanupResult */ /** - * @typedef {Object} CleanupResult + * @alias {object} CleanupResult */ /** * Remove any non-permanent and non-manually-created scopes not currently in use. - * @param {*} dbCtx - * @param {Number} atLeastMsSinceLast skip cleanup if already executed this recently - * @returns {Promise} + * @param {*} dbCtx db context + * @param {number} atLeastMsSinceLast skip cleanup if already executed this recently + * @returns {Promise} cleanup result */ async scopeCleanup(dbCtx, atLeastMsSinceLast) { this._notImplemented('scopeClean', arguments); @@ -563,10 +565,10 @@ class Database { /** * Forget tokens after they have expired, and redeemed codes after they have expired. - * @param {*} dbCtx - * @param {Number} codeLifespanSeconds - * @param {Number} atLeastMsSinceLast skip cleanup if already executed this recently - * @returns {Promise} + * @param {*} dbCtx db context + * @param {number} codeLifespanSeconds code lifespan seconds + * @param {number} atLeastMsSinceLast skip cleanup if already executed this recently + * @returns {Promise} cleanup result */ async tokenCleanup(dbCtx, codeLifespanSeconds, atLeastMsSinceLast) { this._notImplemented('tokenCleanup', arguments); @@ -575,9 +577,9 @@ class Database { /** * Look up a redeemed token by code_id. - * @param {*} dbCtx - * @param {String} codeId - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} codeId code id + * @returns {Promise} token */ async tokenGetByCodeId(dbCtx, codeId) { this._notImplemented('tokenGetByCodeId', arguments); @@ -586,8 +588,8 @@ class Database { /** * Sets a redeemed token as revoked. - * @param {*} dbCtx - * @param {String} codeId - uuid + * @param {*} dbCtx db context + * @param {string} codeId - uuid * @returns {Promise} */ async tokenRevokeByCodeId(dbCtx, codeId) { @@ -597,8 +599,8 @@ class Database { /** * Revoke the refreshability of a codeId. - * @param {*} dbCtx - * @param {String} codeId - uuid + * @param {*} dbCtx db context + * @param {string} codeId - uuid * @returns {Promise} */ async tokenRefreshRevokeByCodeId(dbCtx, codeId) { @@ -608,26 +610,27 @@ class Database { /** * Get all tokens assigned to identifier. - * @param {*} dbCtx - * @param {String} identifier - * @returns {Promise} + * @param {*} dbCtx db context + * @param {string} identifier identifier + * @returns {Promise} token */ async tokensGetByIdentifier(dbCtx, identifier) { this._notImplemented('tokensGetByIdentifier', arguments); } - /** @typedef {Object} RedeemedTicketData - * @property {String} subject - * @property {String} resource - * @property {String=} iss - * @property {String} ticket - * @property {String} token + /** + * @typedef {object} RedeemedTicketData + * @property {string} subject subject + * @property {string} resource resource + * @property {string=} iss issuer + * @property {string} ticket ticket + * @property {string} token token */ /** * Persist details of a redeemed ticket. - * @param {*} dbCtx - * @param {RedeemedTicketData} redeemedData + * @param {*} dbCtx db context + * @param {RedeemedTicketData} redeemedData redeemed data * @returns {Promise} */ async ticketRedeemed(dbCtx, redeemedData) { @@ -637,18 +640,20 @@ class Database { /** * Update details of a redeemed ticket that it has been published. - * @param {*} dbCtx - * @param {RedeemedTicketData} redeemedData + * @param {*} dbCtx db context + * @param {RedeemedTicketData} redeemedData redeemed data * @returns {Promise} */ async ticketTokenPublished(dbCtx, redeemedData) { this._notImplemented('ticketTokenPublished', arguments); } + /** * Retrieve redeemed tokens which have not yet been published to queue. - * @param {Number} limit - * @returns {Promise} + * @param {*} dbCtx db context + * @param {number} limit limit + * @returns {Promise} redeemed but not published */ async ticketTokenGetUnpublished(dbCtx, limit) { this._notImplemented('ticketTokenGetUnpublished', arguments); diff --git a/src/db/schema-version-helper.js b/src/db/schema-version-helper.js index 65a1e39..4582a87 100644 --- a/src/db/schema-version-helper.js +++ b/src/db/schema-version-helper.js @@ -10,17 +10,17 @@ const path = require('path'); */ /** - * @typedef {Object} SchemaVersionObject - * @property {Number} major - * @property {Number} minor - * @property {Number} patch + * @typedef {object} SchemaVersionObject + * @property {number} major major + * @property {number} minor minor + * @property {number} patch patch */ /** * Split a dotted version string into parts. - * @param {String} v - * @returns {SchemaVersionObject} + * @param {string} v version string + * @returns {SchemaVersionObject} version object */ function schemaVersionStringToObject(v) { const [ major, minor, patch ] = v.split('.', 3).map((x) => parseInt(x, 10)); @@ -30,8 +30,9 @@ function schemaVersionStringToObject(v) { /** * Render a version object numerically. - * @param {SchemaVersionObject} v - * @returns {Number} + * Assumes no part will be greater than 1000. + * @param {SchemaVersionObject} v version object + * @returns {number} number */ function schemaVersionObjectToNumber(v) { const vScale = 1000; @@ -41,8 +42,8 @@ function schemaVersionObjectToNumber(v) { /** * Convert dotted version string into number. - * @param {String} v - * @returns {Number} + * @param {string} v version string + * @returns {number} number */ function schemaVersionStringToNumber(v) { return schemaVersionObjectToNumber(schemaVersionStringToObject(v)); @@ -51,9 +52,9 @@ function schemaVersionStringToNumber(v) { /** * Version string comparison, for sorting. - * @param {String} a - * @param {String} b - * @returns {Number} + * @param {string} a version string + * @param {string} b version string + * @returns {number} cmp */ function schemaVersionStringCmp(a, b) { return schemaVersionStringToNumber(a) - schemaVersionStringToNumber(b); @@ -62,9 +63,10 @@ function schemaVersionStringCmp(a, b) { /** * Check if an entry in a directory is a directory containing a migration file. - * @param {String} schemaDir - * @param {String} name - * @returns {Boolean} + * @param {string} schemaDir schema dir + * @param {string} name name + * @param {string} migrationFile migration file + * @returns {boolean} is */ function isSchemaMigrationDirectory(schemaDir, name, migrationFile = 'apply.sql') { // eslint-disable-next-line security/detect-non-literal-fs-filename @@ -75,7 +77,7 @@ function isSchemaMigrationDirectory(schemaDir, name, migrationFile = 'apply.sql' // eslint-disable-next-line security/detect-non-literal-fs-filename applyStat = fs.statSync(path.join(schemaDir, name, migrationFile)); return applyStat.isFile(); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars return false; } } @@ -86,8 +88,8 @@ function isSchemaMigrationDirectory(schemaDir, name, migrationFile = 'apply.sql' /** * Return an array of schema migration directory names within engineDir, * sorted in increasing order. - * @param {String} engineDir - * @returns {String[]} + * @param {string} engineDir path to implementation + * @returns {string[]} versions */ function allSchemaVersions(engineDir) { const schemaDir = path.join(engineDir, 'sql', 'schema'); @@ -101,12 +103,12 @@ function allSchemaVersions(engineDir) { /** * Return an array of schema migration directory names within engineDir, * which are within supported range, and are greater than the current - * @param {String} engineDir - * @param {SchemaVersionObject} current - * @param {Object} supported - * @param {SchemaVersionObject} supported.min - * @param {SchemaVersionObject} supported.max - * @returns {String[]} + * @param {string} engineDir path to implementation + * @param {SchemaVersionObject} current version + * @param {object} supported range of supported versions + * @param {SchemaVersionObject} supported.min minimum version + * @param {SchemaVersionObject} supported.max maximum version + * @returns {string[]} applicable versions */ function unappliedSchemaVersions(engineDir, current, supported) { const min = schemaVersionObjectToNumber(supported.min); @@ -128,4 +130,4 @@ module.exports = { isSchemaMigrationDirectory, allSchemaVersions, unappliedSchemaVersions, -}; \ No newline at end of file +}; diff --git a/src/db/sqlite/index.js b/src/db/sqlite/index.js index a618b9a..2dd75b2 100644 --- a/src/db/sqlite/index.js +++ b/src/db/sqlite/index.js @@ -60,8 +60,8 @@ class DatabaseSQLite extends Database { /** * Boolean to 0/1 representation for SQLite params. - * @param {Boolean} bool - * @returns {Number} + * @param {boolean} bool boolean + * @returns {number} number */ static _booleanToNumeric(bool) { // eslint-disable-next-line security/detect-object-injection @@ -84,7 +84,7 @@ class DatabaseSQLite extends Database { let metaExists = tableExists.get(); if (metaExists === undefined) { const fPath = path.join(__dirname, 'sql', 'schema', 'init.sql'); - // eslint-disable-next-line security/detect-non-literal-fs-filename + const fSql = fs.readFileSync(fPath, { encoding: 'utf8' }); this.db.exec(fSql); metaExists = tableExists.get(); @@ -139,7 +139,7 @@ class DatabaseSQLite extends Database { }; }; - // eslint-disable-next-line security/detect-non-literal-fs-filename + for (const f of fs.readdirSync(sqlDir)) { const fPath = path.join(sqlDir, f); const { name: fName, ext: fExt } = path.parse(f); diff --git a/src/logger/data-sanitizers.js b/src/logger/data-sanitizers.js index 5494d27..df653d6 100644 --- a/src/logger/data-sanitizers.js +++ b/src/logger/data-sanitizers.js @@ -2,9 +2,9 @@ /** * Scrub credential from POST login body data. - * @param {Object} data - * @param {Boolean} sanitize - * @returns {Boolean} + * @param {object} data data + * @param {boolean} sanitize do sanitize + * @returns {boolean} did/would sanitize */ function sanitizePostCredential(data, sanitize = true) { let unclean = false; @@ -29,9 +29,9 @@ function sanitizePostCredential(data, sanitize = true) { /** * Scrub sensitive data from context. - * @param {Object} data - * @param {Boolean} sanitize - * @returns {Boolean} + * @param {object} data data + * @param {boolean} sanitize do sanitize + * @returns {boolean} did/would sanitize */ function sanitizeContext(data, sanitize = true) { let unclean = false; @@ -78,8 +78,9 @@ function sanitizeContext(data, sanitize = true) { * Reduce logged data about scopes from profilesScopes. * For all referenced scopes, only include profiles list. * Remove scopes without profile references from scopeIndex. - * @param {Object} data - * @param {Boolean} sanitize + * @param {object} data data + * @param {boolean} sanitize do sanitize + * @returns {boolean} did/would sanitize */ function reduceScopeVerbosity(data, sanitize = true) { let unclean = false; @@ -114,8 +115,8 @@ function reduceScopeVerbosity(data, sanitize = true) { /** * Return any scope entries on an object, and whether sanitization is needed. - * @param {Object=} obj - * @returns {Object} + * @param {object=} obj obj + * @returns {object} obj */ const _scopesFrom = (obj) => { const scopesEntries = Object.entries(obj?.scopeIndex || {}); @@ -130,21 +131,21 @@ const _scopesFrom = (obj) => { /** - * @typedef {[String, Object]} ScopeEntry + * @typedef {[string, object]} ScopeEntry */ /** * Return new list of entries with scrubbed scopeDetails. - * @param {ScopeEntry[]} entries - * @returns {ScopeEntry[]} + * @param {ScopeEntry[]} entries entries + * @returns {ScopeEntry[]} entries */ const _scopeEntriesScrubber = (entries) => entries.map(([scopeName, scopeDetails]) => ([scopeName, { profiles: scopeDetails.profiles }])); /** * Create a new profilesScopes type object with scrubbed scope details. - * @param {ScopeEntry[]} scopesEntries - * @param {ScopeEntry[]} profilesEntries - * @returns {Object} + * @param {ScopeEntry[]} scopesEntries entries + * @param {ScopeEntry[]} profilesEntries entries + * @returns {object} profilesScopes */ const _sanitizeProfilesScopes = (scopesEntries, profilesEntries) => { const referencedScopesEntries = scopesEntries.filter(([_scopeName, scopeDetails]) => scopeDetails?.profiles?.length); // eslint-disable-line no-unused-vars diff --git a/src/manager.js b/src/manager.js index 398b409..ce544b9 100644 --- a/src/manager.js +++ b/src/manager.js @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const common = require('./common'); @@ -18,6 +19,10 @@ const scopeSplitRE = / +/; const supportedCodeChallengeMethods = ['S256', 'SHA256']; +/** + * @typedef {import('node:http')} http + */ + class Manager { constructor(logger, db, options) { this.options = options; @@ -64,12 +69,12 @@ class Manager { /** * Add an error to a session, keeping only the most-severe code, but all descriptions. * This error is sent along on the redirection back to client endpoint. - * @param {Object} ctx - * @param {Object} ctx.session - * @param {String[]=} ctx.session.errorDescriptions - * @param {String=} ctx.session.error - * @param {String} error - * @param {String} errorDescription + * @param {object} ctx context + * @param {object} ctx.session session + * @param {string[]=} ctx.session.errorDescriptions errors + * @param {string=} ctx.session.error error + * @param {string} error error + * @param {string} errorDescription error */ static _setError(ctx, error, errorDescription) { const errorPrecedence = [ // By increasing severity @@ -109,7 +114,7 @@ class Manager { * The authorization server MUST include the HTTP Cache-Control response * header field with a value of no-store in any response * containing tokens, credentials, or other sensitive information. - * @param {http.ServerResponse} res + * @param {http.ServerResponse} res response */ static _sensitiveResponse(res) { Object.entries({ @@ -121,8 +126,8 @@ class Manager { /** * Sets params entries as url search parameters. - * @param {URL} url - * @param {Object} params + * @param {URL} url url + * @param {object} params parameters */ static _setSearchParams(url, params) { Object.entries(params).forEach((param) => url.searchParams.set(...param)); @@ -131,9 +136,8 @@ class Manager { /** * Serve the informational root page. - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getRoot(res, ctx) { const _scope = _fileScope('getRoot'); @@ -146,8 +150,8 @@ class Manager { /** * Serve the metadata for this service. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getMeta(res, ctx) { const _scope = _fileScope('getMeta'); @@ -187,8 +191,8 @@ class Manager { * Process an authorization request from a client. * User has authenticated, check if user matches profile, * present user with consent form. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getAuthorization(res, ctx) { const _scope = _fileScope('getAuthorization'); @@ -306,7 +310,7 @@ class Manager { /** * Validates, fetches, and parses client_id url, populating clientIdentifier with client h-app data. - * @param {Object} ctx + * @param {object} ctx context */ async _clientIdRequired(ctx) { if (ctx.queryParams['client_id']) { @@ -332,7 +336,7 @@ class Manager { /** * Ensure redirect_uri exists and is corroborated by clientIdentifier data. - * @param {Object} ctx + * @param {object} ctx context */ static _redirectURIRequired(ctx) { if (ctx.queryParams['redirect_uri']) { @@ -355,7 +359,7 @@ class Manager { } } } - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars Manager._setError(ctx, 'invalid_request', 'invalid value for parameter \'redirect_uri\''); } } else { @@ -366,7 +370,7 @@ class Manager { /** * response_type must be valid - * @param {Object} ctx + * @param {object} ctx context */ static _responseTypeRequired(ctx) { ctx.session.responseType = ctx.queryParams['response_type']; @@ -383,7 +387,7 @@ class Manager { /** * A state parameter must be present - * @param {Object} ctx + * @param {object} ctx context */ static _stateRequired(ctx) { ctx.session.state = ctx.queryParams['state']; @@ -397,7 +401,7 @@ class Manager { /** * A code_challenge_method must be present and valid - * @param {Object} ctx + * @param {object} ctx context */ _codeChallengeMethodRequired(ctx) { ctx.session.codeChallengeMethod = ctx.queryParams['code_challenge_method']; @@ -416,7 +420,7 @@ class Manager { /** * A code_challenge must be present - * @param {Object} ctx + * @param {object} ctx context */ _codeChallengeRequired(ctx) { ctx.session.codeChallenge = ctx.queryParams['code_challenge']; @@ -435,7 +439,7 @@ class Manager { /** * Scopes may be present, with one known combination limitation - * @param {Object} ctx + * @param {object} ctx context */ _scopeOptional(ctx) { const _scope = _fileScope('_scopeOptional'); @@ -460,14 +464,14 @@ class Manager { /** * Parses me, if provided - * @param {Object} ctx + * @param {object} ctx context */ async _meOptional(ctx) { const me = ctx.queryParams['me']; if (me) { try { ctx.session.me = await this.communication.validateProfile(me); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars ctx.session.me = undefined; } } @@ -476,8 +480,8 @@ class Manager { /** * Ensure authenticated identifier matches profile. - * @param {Object} ctx - * @returns {Boolean} + * @param {object} ctx context + * @returns {boolean} is valid */ _profileValidForIdentifier(ctx) { const _scope = _fileScope('_profileValidForIdentifier'); @@ -493,10 +497,10 @@ class Manager { /** * Get numeric value from form field data. - * @param {*} ctx - * @param {String} field - * @param {String} customField - * @returns {Number=} + * @param {*} ctx context + * @param {string} field field + * @param {string} customField custom field + * @returns {number=} lifespan */ _parseLifespan(ctx, field, customField) { const _scope = _fileScope('_parseLifespan'); @@ -528,8 +532,8 @@ class Manager { /** * Validate any accepted scopes, ensure uniqueness, return as array. - * @param {Object} ctx - * @returns {String=} + * @param {object} ctx context + * @returns {string[]} scopes */ _parseConsentScopes(ctx) { const _scope = _fileScope('_ingestConsentScopes'); @@ -561,8 +565,8 @@ class Manager { /** * Parse and validate selected me is a valid profile option. - * @param {Object} ctx - * @returns {URL} + * @param {object} ctx context + * @returns {URL} url */ _parseConsentMe(ctx) { const _scope = _fileScope('_parseConsentMe'); @@ -585,8 +589,8 @@ class Manager { /** * Get up-to-date profile data from selected profile endpoint. - * @param {Object} ctx - * @returns {Promise} + * @param {object} ctx context + * @returns {Promise} profile data */ async _fetchConsentProfileData(ctx) { const _scope = _fileScope('_fetchConsentProfileData'); @@ -624,8 +628,8 @@ class Manager { * expires-seconds - optional custom lifespan * refresh - optional refresh lifespan * refresh-seconds - optional custom refresh lifespan - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postConsent(res, ctx) { const _scope = _fileScope('postConsent'); @@ -728,8 +732,8 @@ class Manager { /** * Redeem a code for a profile url, and maybe more profile info. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postAuthorization(res, ctx) { const _scope = _fileScope('postAuthorization'); @@ -786,8 +790,8 @@ class Manager { /** * Ingest an incoming authorization redemption request, parsing fields * onto a new session object on the context. - * @param {*} dbCtx - * @param {Object} ctx + * @param {object} ctx context + * @returns {Promise} */ async _ingestPostAuthorizationRequest(ctx) { const _scope = _fileScope('_ingestPostAuthorizationRequest'); @@ -825,7 +829,7 @@ class Manager { /** * Unpack the session data from provided code overtop of context session .. - * @param {Object} ctx + * @param {object} ctx context */ async _restoreSessionFromCode(ctx) { const _scope = _fileScope('_restoreSessionFromCode'); @@ -873,7 +877,7 @@ class Manager { /** * Ensure provided client_id matches session clientId. - * @param {Object} ctx + * @param {object} ctx context */ _checkSessionMatchingClientId(ctx) { const _scope = _fileScope('_checkSessionMatchingClientId'); @@ -883,7 +887,7 @@ class Manager { try { clientId = new URL(clientId); ctx.session.clientId = new URL(ctx.session.clientId); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.debug(_scope, 'un-parsable client_id url', { ctx }); delete ctx.session.clientId; Manager._setError(ctx, 'invalid_request', 'malformed client_id'); @@ -901,7 +905,7 @@ class Manager { /** - * @param {Object} ctx + * @param {object} ctx context */ _checkSessionMatchingRedirectUri(ctx) { const _scope = _fileScope('_checkSessionMatchingClientId'); @@ -911,7 +915,7 @@ class Manager { try { redirectUri = new URL(redirectUri); ctx.session.redirectUri = new URL(ctx.session.redirectUri); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.debug(_scope, 'un-parsable redirect_uri url', { ctx }); delete ctx.session.redirectUri; Manager._setError(ctx, 'invalid_request', 'malformed redirect_url'); @@ -930,9 +934,9 @@ class Manager { /** * Validate grant_type, either persist on session or set error. - * @param {Object} ctx - * @param {String[]} validGrantTypes - * @param {Boolean} treatEmptyAs + * @param {object} ctx context + * @param {string[]} validGrantTypes grant types + * @param {string=} treatEmptyAs grant type */ _checkGrantType(ctx, validGrantTypes = ['authorization_code'], treatEmptyAs = 'authorization_code') { const _scope = _fileScope('_checkGrantType'); @@ -950,7 +954,7 @@ class Manager { /** - * @param {Object} ctx + * @param {object} ctx context */ _checkSessionMatchingCodeVerifier(ctx) { const _scope = _fileScope('_checkSessionMatchingCodeVerifier'); @@ -981,9 +985,9 @@ class Manager { /** * Attempt to revoke a token. - * @param {*} dbCtx - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {*} dbCtx db context + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async _revokeToken(dbCtx, res, ctx) { const _scope = _fileScope('_revokeToken'); @@ -1039,10 +1043,10 @@ class Manager { /** * Legacy token validation flow. - * @param {*} dbCtx - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {*} dbCtx db context + * @param {http.ClientRequest} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async _validateToken(dbCtx, req, res, ctx) { const _scope = _fileScope('_validateToken'); @@ -1075,9 +1079,9 @@ class Manager { /** * Given a list of newly-requested scopes, return a list of scopes * from previousScopes which are not in requestedScopes. - * @param {String[]} previousScopes - * @param {String[]} requestedScopes - * @returns {String[]} + * @param {string[]} previousScopes scopes + * @param {string[]} requestedScopes scopes + * @returns {string[]} scopes */ static _scopeDifference(previousScopes, requestedScopes) { const scopesToRemove = []; @@ -1095,10 +1099,10 @@ class Manager { /** * Redeem a refresh token for a new token. - * @param {*} dbCtx - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {*} dbCtx db context + * @param {http.ClientRequest} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async _refreshToken(dbCtx, req, res, ctx) { const _scope = _fileScope('_refreshToken'); @@ -1192,15 +1196,15 @@ class Manager { /** * Generate a new ticket for later redemption. - * @param {Object} payload - * @param {} payload.subject deliver ticket to this endpoint - * @param {} payload.resource url the redeemed ticket is valid for accessing - * @param {String[]} payload.scopes list of scopes assigned to ticket - * @param {String} payload.identifier user generating ticket - * @param {} payload.profile profile of user generating ticket - * @param {Number} payload.ticketLifespanSeconds ticket redeemable for this long - * @returns {Promise} - */ + * @param {object} payload payload + * @param {string} payload.subject deliver ticket to this endpoint + * @param {string} payload.resource url the redeemed ticket is valid for accessing + * @param {string[]} payload.scopes list of scopes assigned to ticket + * @param {string} payload.identifier user generating ticket + * @param {string} payload.profile profile of user generating ticket + * @param {number} payload.ticketLifespanSeconds ticket redeemable for this long + * @returns {Promise} ticket + */ async _mintTicket({ subject, resource, scopes, identifier, profile, ticketLifespanSeconds }) { const _scope = _fileScope('_mintTicket'); this.logger.debug(_scope, 'called', { subject, resource, scopes, identifier, profile, ticketLifespanSeconds }); @@ -1221,19 +1225,19 @@ class Manager { /** * @typedef Ticket - * @property {String} codeId - * @property {Date} issued - * @property {Date} expires - * @property {URL} subject - * @property {URL} resource - * @property {String[]} scopes - * @property {String} identifier - * @property {URL} profile + * @property {string} codeId code id + * @property {Date} issued issued at + * @property {Date} expires expires at + * @property {URL} subject subject + * @property {URL} resource resource + * @property {string[]} scopes scopes + * @property {string} identifier identifier + * @property {URL} profile profile */ /** * - * @param {String} ticket - * @returns {Promise} + * @param {string} ticket ticket + * @returns {Promise} ticket object */ async _unpackTicket(ticket) { const ticketObj = await this.mysteryBox.unpack(ticket); @@ -1252,10 +1256,11 @@ class Manager { /** * Redeem a ticket for a token. - * @param {*} dbCtx - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {*} dbCtx db context + * @param {http.ClientRequest} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context + * @returns {Promise} */ async _ticketAuthToken(dbCtx, req, res, ctx) { const _scope = _fileScope('_ticketAuthToken'); @@ -1310,10 +1315,10 @@ class Manager { /** * Redeem a code for a token. - * @param {*} dbCtx - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {*} dbCtx db context + * @param {http.ClientRequest} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async _codeToken(dbCtx, req, res, ctx) { const _scope = _fileScope('_codeToken'); @@ -1393,9 +1398,9 @@ class Manager { /** * Issue, refresh, or validate a token. - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ClientRequest} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postToken(req, res, ctx) { const _scope = _fileScope('postToken'); @@ -1451,9 +1456,10 @@ class Manager { * Ingest token from authorization header, setting ctx.bearer.isValid appropriately. * ctx.bearer not set if auth method not recognized. * This is for legacy validation on token endpoint. - * @param {*} dbCtx - * @param {http.ClientRequest} req - * @param {Object} ctx + * @param {*} dbCtx db context + * @param {http.ClientRequest} req request + * @param {object} ctx context + * @returns {Promise} */ async _checkTokenValidationRequest(dbCtx, req, ctx) { const _scope = _fileScope('_checkTokenValidationRequest'); @@ -1468,7 +1474,7 @@ class Manager { }; try { Object.assign(ctx.bearer, await this.mysteryBox.unpack(authString)); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.debug(_scope, 'failed to unpack token', { ctx }); Manager._setError(ctx, 'invalid_request', 'invalid token'); return; @@ -1509,9 +1515,9 @@ class Manager { /** * Accept an unsolicited ticket proffering. - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ClientRequest} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postTicket(req, res, ctx) { const _scope = _fileScope('postTicket'); @@ -1528,14 +1534,14 @@ class Manager { if (iss) { try { new URL(iss); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.debug(_scope, 'unparsable issuer', { ticket, resource, subject, iss, ctx }); // continue, will try resource for metadata } } try { new URL(resource); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.debug(_scope, 'unparsable resource', { ticket, resource, subject, ctx }); throw new ResponseError(Enum.ErrorResponse.BadRequest); } @@ -1562,11 +1568,15 @@ class Manager { } + /** + * @typedef {object} AMQPChannel + * @property {Function} ack ack + */ /** * Process messages from proffered ticket queue. * Attempt to redeem ticket and publish to redeemed token queue. - * @param {AMQPChannel} channel - * @param {Buffer} message + * @param {AMQPChannel} channel channel + * @param {Buffer} message message */ async queuedTicketProcessor(channel, message) { const _scope = _fileScope('queuedTicketProcessor'); @@ -1601,7 +1611,7 @@ class Manager { let resourceUrlObj; try { resourceUrlObj = new URL(resource); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.error(_scope, 'unparsable resource, discarding', { payload }); channel.ack(message); return; @@ -1662,8 +1672,8 @@ class Manager { /** * Validate a token and return data about it. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postIntrospection(res, ctx) { const _scope = _fileScope('postIntrospection'); @@ -1718,8 +1728,8 @@ class Manager { /** * Revoke a token or refresh token. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postRevocation(res, ctx) { const _scope = _fileScope('postRevocation'); @@ -1740,8 +1750,8 @@ class Manager { /** * Profile information for a token. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postUserInfo(res, ctx) { const _scope = _fileScope('postUserInfo'); @@ -1800,8 +1810,8 @@ class Manager { /** * Show admin interface, allowing manipulation of profiles and scopes. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getAdmin(res, ctx) { const _scope = _fileScope('getAdmin'); @@ -1822,8 +1832,8 @@ class Manager { /** * Process admin interface events. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postAdmin(res, ctx) { const _scope = _fileScope('postAdmin'); @@ -1952,8 +1962,8 @@ class Manager { /** * Show ticket proffer interface. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getAdminTicket(res, ctx) { const _scope = _fileScope('getAdminTicket'); @@ -1975,8 +1985,8 @@ class Manager { /** * Handle ticket proffer interface submission. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async postAdminTicket(res, ctx) { const _scope = _fileScope('postAdminTicket'); @@ -1992,7 +2002,7 @@ class Manager { ].forEach((param) => { try { ctx[param.ctxProp] = new URL(ctx.parsedBody[param.bodyParam]); - } catch (e) { + } catch (e) { // eslint-disable-line no-unused-vars this.logger.debug(_scope, `invalid ${param.bodyParam}`, { ctx }); ctx.errors.push(param.err); } @@ -2087,8 +2097,8 @@ class Manager { /** * Report on generally uninteresting backend information. * Also allow a few event invocations. - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getAdminMaintenance(res, ctx) { const _scope = _fileScope('getAdminMaintenance'); @@ -2125,8 +2135,8 @@ class Manager { /** * - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async getHealthcheck(res, ctx) { const _scope = _fileScope('getHealthcheck'); diff --git a/src/service.js b/src/service.js index dc131c3..f3b7be0 100644 --- a/src/service.js +++ b/src/service.js @@ -9,14 +9,17 @@ const path = require('path'); const { Dingus } = require('@squeep/api-dingus'); const common = require('./common'); const Manager = require('./manager'); -const { Authenticator, SessionManager } = require('@squeep/authentication-module'); -const { ResourceAuthenticator } = require('@squeep/resource-authentication-module'); +const { Authenticator, ResourceAuthenticator, SessionManager } = require('@squeep/authentication-module'); const { initContext, navLinks } = require('./template/template-helper'); const Enum = require('./enum'); const { ResponseError } = require('./errors'); const _fileScope = common.fileScope(__filename); +/** + * @typedef {import('node:http')} http + */ + class Service extends Dingus { constructor(logger, db, options, asyncLocalStorage) { super(logger, { @@ -103,9 +106,9 @@ class Service extends Dingus { /** * Do a little more on each request. - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async preHandler(req, res, ctx) { const _scope = _fileScope('preHandler'); @@ -125,9 +128,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAdminLogin(req, res, ctx) { const _scope = _fileScope('handlerGetAdminLogin'); @@ -142,9 +145,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostAdminLogin(req, res, ctx) { const _scope = _fileScope('handlerPostAdminLogin'); @@ -163,9 +166,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAdminSettings(req, res, ctx) { const _scope = _fileScope('handlerGetAdminSettings'); @@ -182,9 +185,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostAdminSettings(req, res, ctx) { const _scope = _fileScope('handlerPostAdminSettings'); @@ -202,9 +205,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAdminLogout(req, res, ctx) { const _scope = _fileScope('handlerGetAdminLogout'); @@ -221,9 +224,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAdmin(req, res, ctx) { const _scope = _fileScope('handlerGetAdmin'); @@ -240,9 +243,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostAdmin(req, res, ctx) { const _scope = _fileScope('handlerPostAdmin'); @@ -260,9 +263,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAdminTicket(req, res, ctx) { const _scope = _fileScope('handlerGetAdminTicket'); @@ -279,9 +282,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostAdminTicket(req, res, ctx) { const _scope = _fileScope('handlerPostAdminTicket'); @@ -299,9 +302,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetMeta(req, res, ctx) { const _scope = _fileScope('handlerGetMeta'); @@ -321,9 +324,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAuthorization(req, res, ctx) { const _scope = _fileScope('handlerGetAuthorization'); @@ -340,9 +343,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostAuthorization(req, res, ctx) { const _scope = _fileScope('handlerPostAuthorization'); @@ -364,9 +367,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostConsent(req, res, ctx) { const _scope = _fileScope('handlerPostConsent'); @@ -386,9 +389,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostTicket(req, res, ctx) { const _scope = _fileScope('handlerPostTicket'); @@ -408,9 +411,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostToken(req, res, ctx) { const _scope = _fileScope('handlerPostToken'); @@ -430,9 +433,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostRevocation(req, res, ctx) { const _scope = _fileScope('handlerPostRevocation'); @@ -452,9 +455,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostIntrospection(req, res, ctx) { const _scope = _fileScope('handlerPostIntrospection'); @@ -476,9 +479,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerPostUserInfo(req, res, ctx) { const _scope = _fileScope('handlerPostUserInfo'); @@ -498,9 +501,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetRoot(req, res, ctx) { const _scope = _fileScope('handlerGetRoot'); @@ -521,6 +524,9 @@ class Service extends Dingus { /** * Temporary to see what an unsolicited payload contains. + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerWhaGwan(req, res, ctx) { this.setResponseType(this.responseTypes, req, res, ctx); @@ -529,9 +535,9 @@ class Service extends Dingus { } /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetHealthcheck(req, res, ctx) { const _scope = _fileScope('handlerGetHealthcheck'); @@ -544,9 +550,9 @@ class Service extends Dingus { /** - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerGetAdminMaintenance(req, res, ctx) { const _scope = _fileScope('handlerGetAdminMaintenance'); @@ -567,9 +573,9 @@ class Service extends Dingus { * Intercept this and redirect if we have enough information, otherwise default to framework. * Fixing this will likely have to wait until an e2e test framework is in place. * The redirect attempt should probably be contained in a Manager method, but here it is for now. - * @param {http.IncomingMessage} req - * @param {http.ServerResponse} res - * @param {Object} ctx + * @param {http.IncomingMessage} req request + * @param {http.ServerResponse} res response + * @param {object} ctx context */ async handlerInternalServerError(req, res, ctx) { const _scope = _fileScope('handlerInternalServerError'); diff --git a/src/template/admin-html.js b/src/template/admin-html.js index 3b16a28..f72102f 100644 --- a/src/template/admin-html.js +++ b/src/template/admin-html.js @@ -10,11 +10,23 @@ const th = require('./template-helper'); const { sessionNavLinks } = require('@squeep/authentication-module'); +/** + * + * @param {string} profile profile + * @returns {string} li + */ function renderProfileLI(profile) { return `\t
  • ${profile}
  • `; } +/** + * + * @param {string} profile profile + * @param {string} scope scope + * @param {boolean} selected is selected + * @returns {string} td + */ function renderProfileScopeIndicator(profile, scope, selected) { const checked = selected ? ' checked' : ''; return `\t\t @@ -22,6 +34,13 @@ function renderProfileScopeIndicator(profile, scope, selected) { \t\t`; } +/** + * + * @param {string} scope scope + * @param {object} details details + * @param {string[]} profiles profiles + * @returns {string} tr + */ function renderScopeRow(scope, details, profiles) { return `\t ${(profiles || []).map((profile) => renderProfileScopeIndicator(profile, scope, details.profiles.includes(profile))).join('\n')} @@ -37,6 +56,11 @@ ${(profiles || []).map((profile) => renderProfileScopeIndicator(profile, scope, } +/** + * + * @param {string} profile profile + * @returns {string} th + */ function renderProfileHeader(profile) { return ` \t\t${profile} @@ -44,6 +68,12 @@ function renderProfileHeader(profile) { } +/** + * + * @param {object} scopeIndex scopes + * @param {string[]} profiles profiles + * @returns {string} table + */ function scopeIndexTable(scopeIndex, profiles) { return ` @@ -61,6 +91,11 @@ ${Object.entries(scopeIndex).sort(th.scopeCompare).map(([scope, details]) => ren
    `; } +/** + * + * @param {object} token token + * @returns {string} type + */ function _tokenType(token) { if (token.resource) { return 'ticket-token'; @@ -71,6 +106,11 @@ function _tokenType(token) { return 'token'; } +/** + * + * @param {object} token token + * @returns {string} tr + */ function renderTokenRow(token) { const createdTitle = token.refreshed ? 'Refreshed At' : 'Created At'; const createdDate = token.refreshed ? token.refreshed : token.created; @@ -91,12 +131,20 @@ function renderTokenRow(token) { \t\t`; } +/** + * @returns {string} tr + */ function noTokensRows() { return [`\t\t \t\t\t(No active or recent tokens.) \t\t`]; } +/** + * + * @param {object} tokens tokens + * @returns {string} table + */ function tokenTable(tokens) { const tokenRows = tokens?.length ? tokens.map((token) => renderTokenRow(token)) : noTokensRows(); const formOpen = tokens?.length ? '
    \n' : ''; @@ -122,6 +170,11 @@ ${tokenRows.join('\n')} ${formClose}`; } +/** + * + * @param {object} ctx context + * @returns {string} section + */ function mainContent(ctx) { const profileList = (ctx.profilesScopes?.profiles || []).map((p) => renderProfileLI(p)).join('\n'); return `
    @@ -186,16 +239,16 @@ ${tokenTable(ctx.tokens)} /** * - * @param {Object} ctx - * @param {Object} ctx.profilesScopes.scopeIndex - * @param {String[]} ctx.profilesScopes.profiles - * @param {Object[]} ctx.tokens - * @param {Object} options - * @param {Object} options.manager - * @param {String} options.manager.pageTitle - * @param {String} options.manager.logoUrl - * @param {String[]} options.manager.footerEntries - * @returns {String} + * @param {object} ctx context + * @param {object} ctx.profilesScopes.scopeIndex scopes + * @param {string[]} ctx.profilesScopes.profiles profiles + * @param {object[]} ctx.tokens tokens + * @param {object} options options + * @param {object} options.manager manager options + * @param {string} options.manager.pageTitle page title + * @param {string} options.manager.logoUrl logo url + * @param {string[]} options.manager.footerEntries footer entries + * @returns {string} page */ module.exports = (ctx, options) => { const pagePathLevel = 1; diff --git a/src/template/admin-maintenance-html.js b/src/template/admin-maintenance-html.js index 764b4d3..0f4de44 100644 --- a/src/template/admin-maintenance-html.js +++ b/src/template/admin-maintenance-html.js @@ -3,6 +3,11 @@ const th = require('./template-helper'); const { sessionNavLinks } = require('@squeep/authentication-module'); +/** + * + * @param {object} entry entry + * @returns {string} tr + */ function renderAlmanacRow(entry) { const { event, date } = entry; return ` @@ -11,6 +16,11 @@ function renderAlmanacRow(entry) { `; } +/** + * + * @param {object[]} almanac entries + * @returns {string} section + */ function almanacSection(almanac) { return `
    \t

    Almanac

    @@ -28,6 +38,12 @@ ${almanac.map((entry) => renderAlmanacRow(entry)).join('\n')}
    `; } +/** + * + * @param {string} choreName name + * @param {object} choreDetails details + * @returns {string} tr + */ function renderChoreRow(choreName, choreDetails) { const { intervalMs, nextSchedule } = choreDetails; return ` @@ -37,6 +53,11 @@ function renderChoreRow(choreName, choreDetails) { `; } +/** + * + * @param {object} chores chores + * @returns {string} section + */ function choresSection(chores) { return `
    \t

    Chores

    @@ -57,15 +78,14 @@ ${Object.entries(chores).map((chore) => renderChoreRow(...chore)).join('\n')} /** * - * @param {Object} ctx - * @param {Object[]} ctx.almanac - * @param {Object} ctx.chores - * @param {Object} options - * @param {Object} options.manager - * @param {String} options.manager.pageTitle - * @param {String[]} options.manager.footerEntries - * @param {String} options.adminContactHTML - * @returns {String} + * @param {object} ctx context + * @param {object[]} ctx.almanac entries + * @param {object} ctx.chores chores + * @param {object} options options + * @param {object} options.manager manager options + * @param {string} options.manager.pageTitle page title + * @param {string[]} options.manager.footerEntries footer entires + * @returns {string} page */ module.exports = (ctx, options) => { const pagePathLevel = 1; diff --git a/src/template/admin-ticket-html.js b/src/template/admin-ticket-html.js index 4bb2d06..75d7ce7 100644 --- a/src/template/admin-ticket-html.js +++ b/src/template/admin-ticket-html.js @@ -8,10 +8,20 @@ const th = require('./template-helper'); const { sessionNavLinks } = require('@squeep/authentication-module'); +/** + * + * @param {string} profile profile + * @returns {string} option + */ function renderProfileOption(profile) { return ``; } +/** + * + * @param {string} scope scope + * @returns {string} tr + */ function renderScopeCheckboxTR(scope) { const defaultChecked = ['read']; const checked = defaultChecked.includes(scope) ? ' checked' : ''; @@ -21,6 +31,11 @@ function renderScopeCheckboxTR(scope) { `; } +/** + * + * @param {object} ctx context + * @returns {string} section + */ function mainContent(ctx) { const profileOptions = th.indented(4, (ctx?.profilesScopes?.profiles || []).map((profile) => renderProfileOption(profile))) .join('\n'); @@ -71,15 +86,15 @@ ${scopesCheckboxRows} /** * - * @param {Object} ctx - * @param {Object} ctx.profilesScopes.scopeIndex - * @param {String[]} ctx.profileScopes.profiles - * @param {Object} options - * @param {Object} options.manager - * @param {String} options.manager.pageTitle - * @param {String} options.manager.logoUrl - * @param {String[]} options.manager.footerEntries - * @returns {String} + * @param {object} ctx context + * @param {object} ctx.profilesScopes.scopeIndex scopes structure + * @param {string[]} ctx.profileScopes.profiles profile + * @param {object} options options + * @param {object} options.manager manager options + * @param {string} options.manager.pageTitle page title + * @param {string} options.manager.logoUrl logo url + * @param {string[]} options.manager.footerEntries footer entries + * @returns {string} page */ module.exports = (ctx, options) => { const pagePathLevel = 1; diff --git a/src/template/authorization-error-html.js b/src/template/authorization-error-html.js index 16e3c48..83d9526 100644 --- a/src/template/authorization-error-html.js +++ b/src/template/authorization-error-html.js @@ -5,15 +5,15 @@ const { sessionNavLinks } = require('@squeep/authentication-module'); /** * - * @param {Object} ctx - * @param {Object} ctx.session - * @param {String=} ctx.session.error - * @param {String[]=} ctx.session.errorDescriptions - * @param {Object} options - * @param {Object} options.manager - * @param {String} options.manager.pageTitle - * @param {String} options.manager.footerEntries - * @returns {String} + * @param {object} ctx context + * @param {object} ctx.session session + * @param {string=} ctx.session.error errors + * @param {string[]=} ctx.session.errorDescriptions errors + * @param {object} options options + * @param {object} options.manager manager options + * @param {string} options.manager.pageTitle page title + * @param {string} options.manager.footerEntries footer entries + * @returns {string} page */ module.exports = (ctx, options) => { const pagePathLevel = 0; diff --git a/src/template/authorization-request-html.js b/src/template/authorization-request-html.js index ac2ea06..b23fb92 100644 --- a/src/template/authorization-request-html.js +++ b/src/template/authorization-request-html.js @@ -4,13 +4,13 @@ const th = require('./template-helper'); const { sessionNavLinks } = require('@squeep/authentication-module'); /** - * @param {Object} hApp - * @param {Object} hApp.properties - * @param {String[]=} hApp.properties.url - * @param {String[]=} hApp.properties.summary - * @param {String[]=} hApp.properties.logo - * @param {String[]=} hApp.properties.name - * @returns {String} + * @param {object} hApp client identifier h-app + * @param {object} hApp.properties properties + * @param {string[]=} hApp.properties.url url + * @param {string[]=} hApp.properties.summary summary + * @param {string[]=} hApp.properties.logo logo + * @param {string[]=} hApp.properties.name name + * @returns {string} span */ function renderClientIdentifierProperties(hApp) { const properties = hApp.properties || {}; @@ -47,9 +47,9 @@ function renderClientIdentifierProperties(hApp) { /** - * @param {Object} clientIdentifier - * @param {Object[]} clientIdentifier.items - * @returns {String} + * @param {object} clientIdentifier client identifier + * @param {object[]} clientIdentifier.items items + * @returns {string} spans */ function renderClientIdentifier(clientIdentifier) { const hAppEntries = clientIdentifier?.items || []; @@ -58,9 +58,9 @@ function renderClientIdentifier(clientIdentifier) { /** - * @param {String} profile - * @param {Boolean} selected - * @returns {String} + * @param {string} profile profile + * @param {boolean} selected is selected + * @returns {string} option */ function renderProfileOption(profile, selected) { return ``; @@ -68,9 +68,9 @@ function renderProfileOption(profile, selected) { /** - * @param {String[]} availableProfiles - * @param {String} hintProfile - * @returns {String} + * @param {string[]} availableProfiles profiles + * @param {string} hintProfile profile + * @returns {string} fieldset */ function renderProfileFieldset(availableProfiles, hintProfile) { if (!availableProfiles || availableProfiles.length <= 1) { @@ -93,12 +93,16 @@ ${availableProfiles.map((profile) => renderProfileOption(profile, profile === hi /** - * @param {ScopeDetails} scope - * @param {String} scope.scope - * @param {String} scope.description - * @param {String[]} scope.profiles - * @param {Boolean} checked - * @returns {String} + * @typedef {object} ScopeDetails + * @property {string} scope scope + * @property {string} description description + * @property {string[]} profiles profiles + */ + +/** + * @param {ScopeDetails} scope scope details + * @param {boolean} checked is checked + * @returns {string} scope li */ function renderScopeCheckboxLI(scope, checked) { let scopeDescription; @@ -122,6 +126,11 @@ function renderScopeCheckboxLI(scope, checked) { } +/** + * + * @param {ScopeDetails[]=} requestedScopes scope details + * @returns {string} fieldset + */ function renderRequestedScopes(requestedScopes) { if (!requestedScopes?.length) { return ''; @@ -140,8 +149,8 @@ ${requestedScopes.map((scopeDetails) => renderScopeCheckboxLI(scopeDetails, true } /** - * @param {ScopeDetails[]} additionalScopes - * @returns {String} + * @param {ScopeDetails[]} additionalScopes scopes + * @returns {string} fieldset */ function renderAdditionalScopes(additionalScopes) { const parts = []; @@ -173,6 +182,8 @@ ${additionalScopes.map((scopeDetails) => renderScopeCheckboxLI(scopeDetails, fal /** * + * @param {string[]} requestedScopes scopes + * @returns {string} fieldset */ function renderExpiration(requestedScopes) { const tokenableScopes = requestedScopes.filter((s) => !['profile', 'email'].includes(s)); @@ -215,6 +226,15 @@ function renderExpiration(requestedScopes) { \t`; } +/** + * + * @param {string} name name + * @param {string} value value + * @param {string} label label + * @param {boolean} checked is checked + * @param {number} indent indent + * @returns {string} div + */ function radioButton(name, value, label, checked = false, indent = 0) { const id = `${name}-${value}`; return th.indented(indent, [ @@ -225,27 +245,31 @@ function radioButton(name, value, label, checked = false, indent = 0) { ]).join(''); } +/** + * @alias {object} ScopeIndex + */ + /** * - * @param {Object} ctx - * @param {Object[]} ctx.notifications - * @param {Object} ctx.session - * @param {String[]=} ctx.session.scope - * @param {URL=} ctx.session.me - * @param {String[]} ctx.session.profiles - * @param {ScopeIndex} ctx.session.scopeIndex - * @param {Object} ctx.session.clientIdentifier - * @param {Object[]} ctx.session.clientIdentifier.items - * @param {Object} ctx.session.clientIdentifier.items.properties - * @param {String[]=} ctx.session.clientIdentifier.items.properties.url - * @param {String[]=} ctx.session.clientIdentifier.items.properties.summary - * @param {String[]=} ctx.session.clientIdentifier.items.properties.logo - * @param {String[]=} ctx.session.clientIdentifier.items.properties.name - * @param {String} ctx.session.clientId - * @param {String} ctx.session.persist - * @param {String} ctx.session.redirectUri - * @param {Object} options - * @returns {String} + * @param {object} ctx context + * @param {object[]} ctx.notifications notifications + * @param {object} ctx.session session + * @param {string[]=} ctx.session.scope scopes + * @param {URL=} ctx.session.me profile + * @param {string[]} ctx.session.profiles profiles + * @param {ScopeIndex} ctx.session.scopeIndex scopes structure + * @param {object} ctx.session.clientIdentifier client identifier + * @param {object[]} ctx.session.clientIdentifier.items items + * @param {object} ctx.session.clientIdentifier.items.properties properties + * @param {string[]=} ctx.session.clientIdentifier.items.properties.url url + * @param {string[]=} ctx.session.clientIdentifier.items.properties.summary sumamry + * @param {string[]=} ctx.session.clientIdentifier.items.properties.logo logo + * @param {string[]=} ctx.session.clientIdentifier.items.properties.name name + * @param {string} ctx.session.clientId client id + * @param {string} ctx.session.persist persist + * @param {string} ctx.session.redirectUri redirect + * @param {object} options options + * @returns {string} section */ function mainContent(ctx, options) { // eslint-disable-line no-unused-vars const session = ctx.session || {}; @@ -283,7 +307,7 @@ function mainContent(ctx, options) { // eslint-disable-line no-unused-vars return [ `
    -\tThe application client ${renderClientIdentifier(session.clientIdentifier)} at ${session.clientId} would like to identify you as ${hintedProfile ? hintedProfile : '(unspecified)'}. +\tThe application client ${renderClientIdentifier(session.clientIdentifier)} at ${session.clientId} would like to identify you as ${hintedProfile || '(unspecified)'}.
    \t`, @@ -310,21 +334,21 @@ function mainContent(ctx, options) { // eslint-disable-line no-unused-vars /** * - * @param {Object} ctx - * @param {Object} ctx.session - * @param {String[]=} ctx.session.scope - * @param {URL=} ctx.session.me - * @param {String[]} ctx.session.profiles - * @param {ScopeIndex} ctx.session.scopeIndex - * @param {Object} ctx.session.clientIdentifier - * @param {String} ctx.session.clientId - * @param {String} ctx.session.persist - * @param {String} ctx.session.redirectUri - * @param {Object} options - * @param {Object} options.manager - * @param {String} options.manager.pageTitle - * @param {String} options.manager.footerEntries - * @returns {String} + * @param {object} ctx context + * @param {object} ctx.session session object + * @param {string[]=} ctx.session.scope scopes + * @param {URL=} ctx.session.me url + * @param {string[]} ctx.session.profiles profiles + * @param {ScopeIndex} ctx.session.scopeIndex scopes structure + * @param {object} ctx.session.clientIdentifier client identifier + * @param {string} ctx.session.clientId client id + * @param {string} ctx.session.persist persist + * @param {string} ctx.session.redirectUri redirect url + * @param {object} options options + * @param {object} options.manager manager options + * @param {string} options.manager.pageTitle page title + * @param {string} options.manager.footerEntries footer entries + * @returns {string} page */ module.exports = (ctx, options) => { const pagePathLevel = 0; diff --git a/src/template/root-html.js b/src/template/root-html.js index 2b7abfe..c6095dc 100644 --- a/src/template/root-html.js +++ b/src/template/root-html.js @@ -3,6 +3,9 @@ const th = require('./template-helper'); const { sessionNavLinks } = require('@squeep/authentication-module'); +/** + * @returns {string} section + */ function aboutSection() { return `
    @@ -19,6 +22,10 @@ function aboutSection() {
    `; } +/** + * @param {string} contactHTML content + * @returns {string} section + */ function contactSection(contactHTML) { let section = ''; if (contactHTML) { @@ -31,13 +38,13 @@ ${contactHTML} /** * - * @param {Object} ctx - * @param {Object} options - * @param {Object} options.manager - * @param {String} options.manager.pageTitle - * @param {String[]} options.manager.footerEntries - * @param {String} options.adminContactHTML - * @returns {String} + * @param {object} ctx context + * @param {object} options options + * @param {object} options.manager manager options + * @param {string} options.manager.pageTitle page title + * @param {string[]} options.manager.footerEntries footer entries + * @param {string=} options.adminContactHTML content + * @returns {string} page */ module.exports = (ctx, options) => { const pagePathLevel = 0; diff --git a/src/template/template-helper.js b/src/template/template-helper.js index f15d3e6..4c0ad9d 100644 --- a/src/template/template-helper.js +++ b/src/template/template-helper.js @@ -5,8 +5,8 @@ const { TemplateHelper } = require('@squeep/html-template-helper'); /** * Escape a string to be suitable as a CSS name. - * @param {String} unsafeName - * @returns {String} + * @param {string} unsafeName unsafe name + * @returns {string} escaped name */ function escapeCSS(unsafeName) { return unsafeName.replace(/([^0-9a-zA-Z-])/g, '\\$1'); @@ -18,9 +18,9 @@ function escapeCSS(unsafeName) { * return the comparison between the two, for sorting. * Scopes are sorted such that they are grouped by application, then name. * Empty applications are sorted ahead of extant applications. - * @param {Array} a - * @param {Array} b - * @returns {Number} + * @param {[string, object]} a [scopeName, scopeDetails] + * @param {[string, object]} b [scopeName, scopeDetails] + * @returns {number} comparison */ function scopeCompare([aScope, aDetails], [bScope, bDetails]) { const { application: aApp } = aDetails; @@ -49,9 +49,9 @@ function scopeCompare([aScope, aDetails], [bScope, bDetails]) { /** * Populate common navLinks for page templates. - * @param {Number} pagePathLevel - * @param {Object} ctx - * @param {Object} options + * @param {number} pagePathLevel depth from root + * @param {object} ctx context + * @param {object} options options */ function navLinks(pagePathLevel, ctx, options) { if (!options.navLinks) { diff --git a/test/src/chores.js b/test/src/chores.js index 0272990..1656088 100644 --- a/test/src/chores.js +++ b/test/src/chores.js @@ -1,5 +1,3 @@ -/* eslint-env mocha */ -/* eslint-disable node/no-unpublished-require */ 'use strict'; const assert = require('assert'); diff --git a/test/src/db/abstract.js b/test/src/db/abstract.js index bf74e97..b808507 100644 --- a/test/src/db/abstract.js +++ b/test/src/db/abstract.js @@ -1,8 +1,8 @@ -/* eslint-env mocha */ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const StubDatabase = require('../../stub-db'); const StubLogger = require('../../stub-logger'); diff --git a/test/src/db/factory.js b/test/src/db/factory.js index 31fdd8d..0d706dd 100644 --- a/test/src/db/factory.js +++ b/test/src/db/factory.js @@ -2,7 +2,7 @@ 'use strict'; const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const Logger = require('@squeep/logger-json-console'); const DB = require('../../../src/db'); const DBErrors = require('../../../src/db/errors'); diff --git a/test/src/db/integration.js b/test/src/db/integration.js index 10e88df..c26e277 100644 --- a/test/src/db/integration.js +++ b/test/src/db/integration.js @@ -1,5 +1,4 @@ -/* eslint-env mocha */ -/* eslint-disable sonarjs/no-identical-functions */ +/* eslint-disable security/detect-object-injection */ 'use strict'; /** @@ -17,7 +16,7 @@ */ const assert = require('assert'); -const { step } = require('mocha-steps'); // eslint-disable-line node/no-unpublished-require +const { step } = require('mocha-steps'); const StubLogger = require('../../stub-logger'); // const DBErrors = require('../../../src/db/errors'); // const testData = require('../../test-data/db-integration'); diff --git a/test/src/db/postgres.js b/test/src/db/postgres.js index c00daad..caa6652 100644 --- a/test/src/db/postgres.js +++ b/test/src/db/postgres.js @@ -1,12 +1,10 @@ -/* eslint-disable sonarjs/no-identical-functions */ -/* eslint-env mocha */ /* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; /* This provides implementation coverage, stubbing pg-promise. */ const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const StubLogger = require('../../stub-logger'); const StubDatabase = require('../../stub-db'); const DB = require('../../../src/db/postgres'); @@ -281,7 +279,7 @@ describe('DatabasePostgres', function () { assert(db.db.batch.called); }); it('failure', async function () { - sinon.stub(db.db, 'tx').rejects(expectedException) + sinon.stub(db.db, 'tx').rejects(expectedException); await assert.rejects(() => db._purgeTables(true), expectedException); }); }); // _purgeTables @@ -318,7 +316,7 @@ describe('DatabasePostgres', function () { let event, date; beforeEach(function () { event = 'test_event'; - date = new Date('Fri Dec 22 03:27 UTC 2023') + date = new Date('Fri Dec 22 03:27 UTC 2023'); }); it('success', async function () { const dbResult = { @@ -823,7 +821,7 @@ describe('DatabasePostgres', function () { duration: 22, }; sinon.stub(db.db, 'result').resolves(dbResult); - await db.resourceUpsert(dbCtx, resourceId, secret, description) + await db.resourceUpsert(dbCtx, resourceId, secret, description); }); it('failure', async function () { const dbResult = { diff --git a/test/src/db/schema-version-helper.js b/test/src/db/schema-version-helper.js index cc876d4..2859d30 100644 --- a/test/src/db/schema-version-helper.js +++ b/test/src/db/schema-version-helper.js @@ -1,8 +1,7 @@ -/* eslint-env mocha */ 'use strict'; const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const fs = require('fs'); const svh = require('../../../src/db/schema-version-helper'); @@ -121,6 +120,7 @@ describe('SchemaVersionHelper', function () { .onCall(i++).returns(notDir) // 'init.sql' .onCall(i++).returns(isDir).onCall(i++).returns(isMig) // '1.0.1' .onCall(i++).returns(isDir).onCall(i++).returns(isMig) // '1.0.0' + ; const result = svh.allSchemaVersions('path'); assert.deepStrictEqual(result, expected); }); @@ -151,9 +151,10 @@ describe('SchemaVersionHelper', function () { .onCall(i++).returns(notDir) // 'init.sql' .onCall(i++).returns(isDir).onCall(i++).returns(isMig) // '1.0.1' .onCall(i++).returns(isDir).onCall(i++).returns(isMig) // '1.0.0' + ; const result = svh.unappliedSchemaVersions('path', current, supported); assert.deepStrictEqual(result, expected); }); }); // unappliedSchemaVersions -}); \ No newline at end of file +}); diff --git a/test/src/db/sqlite.js b/test/src/db/sqlite.js index f2d9546..46163b8 100644 --- a/test/src/db/sqlite.js +++ b/test/src/db/sqlite.js @@ -1,12 +1,10 @@ -/* eslint-disable sonarjs/no-identical-functions */ -/* eslint-env mocha */ /* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; /* This provides implementation coverage, stubbing parts of better-sqlite3. */ const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const StubDatabase = require('../../stub-db'); const StubLogger = require('../../stub-logger'); const DB = require('../../../src/db/sqlite'); @@ -112,7 +110,7 @@ describe('DatabaseSQLite', function () { db._optimize(); assert(db.db.pragma.called); assert(db.statement._optimize.all.called); - assert.strictEqual(db.changesSinceLastOptimize, 0n) + assert.strictEqual(db.changesSinceLastOptimize, 0n); }); }); // _optimize @@ -220,7 +218,7 @@ describe('DatabaseSQLite', function () { let event, date, dbResult; beforeEach(function () { event = 'test_event'; - date = new Date('Fri Dec 22 03:27 UTC 2023') + date = new Date('Fri Dec 22 03:27 UTC 2023'); sinon.stub(db.statement.almanacUpsert, 'run'); dbResult = { changes: 1, @@ -574,7 +572,7 @@ describe('DatabaseSQLite', function () { expires: new Date(refreshResponse.expires * 1000), refreshExpires: new Date(refreshResponse.refreshExpires * 1000), scopes: ['blah'], - } + }; const response = db.refreshCode(dbCtx, codeId, refreshed, removeScopes); assert.deepStrictEqual(response, expectedResponse); }); @@ -584,7 +582,7 @@ describe('DatabaseSQLite', function () { const expectedResponse = { expires: new Date(refreshResponse.expires * 1000), refreshExpires: new Date(refreshResponse.refreshExpires * 1000), - } + }; removeScopes = []; const response = db.refreshCode(dbCtx, codeId, refreshed, removeScopes); assert.deepStrictEqual(response, expectedResponse); @@ -596,7 +594,7 @@ describe('DatabaseSQLite', function () { expires: new Date(refreshResponse.expires * 1000), refreshExpires: new Date(refreshResponse.refreshExpires * 1000), scopes: [], - } + }; const response = db.refreshCode(dbCtx, codeId, refreshed, removeScopes); assert.deepStrictEqual(response, expectedResponse); }); @@ -723,7 +721,7 @@ describe('DatabaseSQLite', function () { sinon.stub(db.statement.scopeInUse, 'get'); dbGetResult = { inUse: false, - } + }; sinon.stub(db.statement.scopeDelete, 'run'); dbRunResult = { changes: 1, @@ -898,7 +896,7 @@ describe('DatabaseSQLite', function () { let dbResult, codeId; beforeEach(function () { codeId = '2f226616-3e79-11ec-ad0f-0025905f714a'; - sinon.stub(db.statement.tokenRevokeByCodeId, 'run') + sinon.stub(db.statement.tokenRevokeByCodeId, 'run'); dbResult = { changes: 1, lastInsertRowid: undefined, @@ -1049,7 +1047,7 @@ describe('DatabaseSQLite', function () { const dbResultAlmanac = { ...dbResult, changes: 0, - } + }; db.statement.ticketTokenPublished.run.returns(dbResult); db.statement.almanacUpsert.run.returns(dbResultAlmanac); assert.throws(() => db.ticketTokenPublished(dbCtx, redeemedData), DBErrors.UnexpectedResult); diff --git a/test/src/logger.js b/test/src/logger.js index cb5914b..e28308c 100644 --- a/test/src/logger.js +++ b/test/src/logger.js @@ -1,8 +1,8 @@ -/* eslint-env mocha */ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const Logger = require('../../src/logger'); const Config = require('../../config'); @@ -70,7 +70,7 @@ describe('Logger', function () { otpConfirmKey: '1234567890123456789012', otpConfirmBox: 'xxxMysteryxxx', otpState: 'xxxMysteryxxx', - } + }, }); assert(logger.backend.info.called); assert(!logger.backend.info.args[0][0].includes('"1234567890123456789012"')); diff --git a/test/src/manager.js b/test/src/manager.js index 66e6f59..f2d708b 100644 --- a/test/src/manager.js +++ b/test/src/manager.js @@ -1,10 +1,9 @@ -/* eslint-env mocha */ -/* eslint-disable capitalized-comments, sonarjs/no-duplicate-string, sonarjs/no-identical-functions */ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const Manager = require('../../src/manager'); const Config = require('../../config'); @@ -1558,13 +1557,13 @@ describe('Manager', function () { manager.mysteryBox.unpack.resolves({}); req.getHeader.returns('Bearer XXX'); await manager._checkTokenValidationRequest(dbCtx, req, ctx); - assert(ctx.session.error) + assert(ctx.session.error); }); it('covers no token', async function () { manager.mysteryBox.unpack.resolves({ c: 'xxx' }); req.getHeader.returns('Bearer XXX'); await manager._checkTokenValidationRequest(dbCtx, req, ctx); - assert(ctx.session.error) + assert(ctx.session.error); }); it('covers db error', async function () { manager.mysteryBox.unpack.resolves({ c: 'xxx' }); diff --git a/test/src/service.js b/test/src/service.js index e555029..d911125 100644 --- a/test/src/service.js +++ b/test/src/service.js @@ -1,10 +1,8 @@ -/* eslint-env mocha */ -/* eslint-disable capitalized-comments */ - +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); -const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require +const sinon = require('sinon'); const { AsyncLocalStorage } = require('node:async_hooks'); const StubDb = require('../stub-db'); @@ -323,4 +321,4 @@ describe('Service', function () { }); }); // handlerWhaGwan -}); \ No newline at end of file +}); diff --git a/test/src/template/admin-html.js b/test/src/template/admin-html.js index 8f5a6c2..bf44d9f 100644 --- a/test/src/template/admin-html.js +++ b/test/src/template/admin-html.js @@ -1,4 +1,4 @@ -/* eslint-env mocha */ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); diff --git a/test/src/template/admin-ticket-html.js b/test/src/template/admin-ticket-html.js index d5eb28f..41e9076 100644 --- a/test/src/template/admin-ticket-html.js +++ b/test/src/template/admin-ticket-html.js @@ -1,4 +1,4 @@ -/* eslint-env mocha */ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); diff --git a/test/src/template/authorization-error-html.js b/test/src/template/authorization-error-html.js index 4858b51..2d1567f 100644 --- a/test/src/template/authorization-error-html.js +++ b/test/src/template/authorization-error-html.js @@ -27,7 +27,7 @@ describe('Authorization Error HTML Template', function () { ctx.session = { error: 'error_name', errorDescriptions: ['something went wrong', 'another thing went wrong'], - } + }; const result = template(ctx, config); await lintHtml(result); assert(result); diff --git a/test/src/template/authorization-request-html.js b/test/src/template/authorization-request-html.js index cefefef..8077310 100644 --- a/test/src/template/authorization-request-html.js +++ b/test/src/template/authorization-request-html.js @@ -1,4 +1,4 @@ -/* eslint-env mocha */ +/* eslint-disable sonarjs/no-duplicate-string */ 'use strict'; const assert = require('assert'); diff --git a/test/stub-db.js b/test/stub-db.js index 054cc47..8eab524 100644 --- a/test/stub-db.js +++ b/test/stub-db.js @@ -1,7 +1,6 @@ -/* eslint-disable security/detect-object-injection */ 'use strict'; -const { StubDatabase: Base } = require('@squeep/test-helper'); // eslint-disable-line node/no-unpublished-require +const { StubDatabase: Base } = require('@squeep/test-helper'); class StubDatabase extends Base { get _stubFns() { diff --git a/test/stub-logger.js b/test/stub-logger.js index 8be142f..c152157 100644 --- a/test/stub-logger.js +++ b/test/stub-logger.js @@ -1,10 +1,10 @@ 'use strict'; -const { StubLogger: Base } = require('@squeep/test-helper'); // eslint-disable-line node/no-unpublished-require +const { StubLogger: Base } = require('@squeep/test-helper'); class StubLogger extends Base { } -module.exports = StubLogger; \ No newline at end of file +module.exports = StubLogger; -- 2.45.2