bump package version to 1.5.0 master v1.5.0
authorJustin Wind <justin.wind+git@gmail.com>
Sun, 12 May 2024 20:18:46 +0000 (13:18 -0700)
committerJustin Wind <justin.wind+git@gmail.com>
Sun, 12 May 2024 20:19:32 +0000 (13:19 -0700)
31 files changed:
.npmrc [new file with mode: 0644]
CHANGELOG.md
README.md
eslint.config.js
index.js
lib/authenticator.js
lib/common.js
lib/errors.js
lib/resource-authenticator.js [new file with mode: 0644]
lib/session-manager.js
lib/stdio-credential.js
lib/template/helpers.js
lib/template/ia-html.js
lib/template/login-html.js
lib/template/otp-html.js
lib/template/settings-html.js
package-lock.json
package.json
test/lib/authenticator.js
test/lib/common.js
test/lib/resource-authenticator.js [new file with mode: 0644]
test/lib/session-manager.js
test/lib/stdio-credential.js
test/lib/template/helpers.js
test/lib/template/ia-html.js
test/lib/template/login-html.js
test/lib/template/otp-html.js
test/lib/template/settings-html.js
test/stub-config.js
test/stub-db.js
test/stub-logger.js

diff --git a/.npmrc b/.npmrc
new file mode 100644 (file)
index 0000000..258cd85
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+message="bump package version to %s"
index c7bf670121c696e8baf0d3ef9418dd86b3d3dc65..47239aa112ce8d344ed05cfe846b49d747293314 100644 (file)
@@ -4,6 +4,15 @@ Releases and notable changes to this project are documented here.
 
 ## [Unreleased]
 
+## [v1.5.0] - 2024-05-12
+
+- Migrated resource authenticator into this package, for inter-server API authentication.
+
+## [v1.4.1] - 2024-05-11
+
+- updated dependencies and devDependencies
+- minor internal refactor
+
 ## [v1.4.0] - 2024-03-23
 
 ### Added
index 6b4400ea56b2285729fd59e3c6fcf307d71a89d4..c4234f651480faa38a3891313b64593e588d0973 100644 (file)
--- a/README.md
+++ b/README.md
@@ -41,6 +41,21 @@ Class providing service handler functions for rendering and processing session l
 
 - `sessionNavLinks` call from app templates to populate navLinks for account settings and logout
 
+### ResourceAuthenticator
+
+Class which fetches and validates resource identifiers and their secrets from database.
+
+Resources are assumed to be other services making API calls.
+
+This is just a time-gated pre-shared-secret HMAC Bearer token scheme.
+
+Default token format is a ':'-separated concatenation of:
+
+- resource identifier, which is a UUID, encoded as 24 bytes in base64url
+- current epoch, encoded as base10 string
+- salt value, randomness encoded as 28 bytes in base64url
+- sha256 HMAC digest of previous data, encoded in base64url
+
 ### Other Notes
 
 For the moment, this imposes a web structure of /admin/* for authentication management paths.
index ebefcac363dd2f0d6db0153dc7dfe9521e648080..fcda56fad771e74e97647ffc52041dcb7b732e28 100644 (file)
@@ -1,110 +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',
-      '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,
 ];
index 5d9da0c4c3b931845ea1edca8fbfdccca3cef3da..b4818641d2c3e45ffb36d71a39254ebc1cfbaa95 100644 (file)
--- a/index.js
+++ b/index.js
@@ -1,12 +1,14 @@
 'use strict';
 
 const Authenticator = require('./lib/authenticator');
+const ResourceAuthenticator = require('./lib/resource-authenticator');
 const SessionManager = require('./lib/session-manager');
 const stdioCredential = require('./lib/stdio-credential');
 const templateHelpers = require('./lib/template/helpers');
 
 module.exports = {
   Authenticator,
+  ResourceAuthenticator,
   SessionManager,
   stdioCredential,
   ...templateHelpers,
index 36c1574b507c223fe380a35ab6f0eb991de085e4..3df0b9db17ccdf763ce13b6340e912b0d6545266 100644 (file)
@@ -16,43 +16,53 @@ const _fileScope = common.fileScope(__filename);
  * Interacts with the authentication database interface.
  */
 
+/**
+ * @typedef {import('node:http')} http
+ */
+
 class Authenticator {
   /**
-   * @typedef {Object} AuthInfo
-   * @property {String} identifier
-   * @property {String} credential
-   * @property {String=} otpKey
+   * @typedef {object} AuthInfo
+   * @property {string} identifier identifier
+   * @property {string} credential hashed credential
+   * @property {string=} otpKey optional otp key
    */
   /**
    * @callback DBContextExec
-   * @param {Object} dbCtx
+   * @param {object} dbCtx
    * @returns {Promise<any>}
    */
   /**
-   * @typedef {Object} AuthDBInterface
-   * @property {(DBContextExec) => Promise<any>} context
-   * @property {(dbCtx: any, identifier: String) => Promise<AuthInfo> } authenticationGet
-   * @property {(dbCtx: any, identifier: String) => Promise<void>} authenticationSuccess
-   * @property {(dbCtx: any, identifier: String, credential: String, otpKey: String=) => Promise<void>} authenticationUpsert
-   * @property {(dbCtx: any, identifier: String, otpKey: String) => Promise<void>} authenticationUpdateOTPKey
-   * @property {(dbCtx: any, identifier: String, credential: AuthInfo) => Promise<void>} authenticationUpdateCredential
+   * @typedef {object} AuthDBInterface
+   * @property {(DBContextExec) => Promise<any>} context db context runner
+   * @property {(dbCtx: any, identifier: string) => Promise<AuthInfo>} authenticationGet get an authentication entry
+   * @property {(dbCtx: any, identifier: string) => Promise<void>} authenticationSuccess store a successful authentication event
+   * @property {(dbCtx: any, identifier: string, credential: string, otpKey: string=) => Promise<void>} authenticationUpsert update an authentication entry
+   * @property {(dbCtx: any, identifier: string, otpKey: string) => Promise<void>} authenticationUpdateOTPKey update otp key
+   * @property {(dbCtx: any, identifier: string, credential: AuthInfo) => Promise<void>} authenticationUpdateCredential update credential
    */
   /**
-   * @param {Console} logger
-   * @param {AuthDBInterface} db
-   * @param {Object} options
-   * @param {String|String[]} options.encryptionSecret
-   * @param {Object} options.authenticator
-   * @param {Boolean} options.authenticator.secureAuthOnly
-   * @param {String[]} options.authenticator.forbiddenPAMIdentifiers
-   * @param {String[]} options.authenticator.authnEnabled in order of preference for storing new credentials
-   * @param {Number=} options.authenticator.inactiveSessionLifespanSeconds
-   * @param {String[]=} options.authenticator.loginBlurb
-   * @param {String[]=} options.authenticator.indieAuthBlurb
-   * @param {String[]=} options.authenticator.userBlurb
-   * @param {String[]=} options.authenticator.otpBlurb
-   * @param {String=} options.dingus
-   * @param {String=} options.dingus.proxyPrefix
+   * @typedef {object} ConsoleLike
+   * @property {Function} debug log debug
+   * @property {Function} error log error
+   * @property {Function} info log info
+   */
+  /**
+   * @param {ConsoleLike} logger logger instance
+   * @param {AuthDBInterface} db db instance
+   * @param {object} options options
+   * @param {string | string[]} options.encryptionSecret encryption secret
+   * @param {object} options.authenticator authenticator options
+   * @param {boolean} options.authenticator.secureAuthOnly disable auth over non-https
+   * @param {string[]} options.authenticator.forbiddenPAMIdentifiers reject these identifiers for PAM auth
+   * @param {string[]} options.authenticator.authnEnabled in order of preference for storing new credentials
+   * @param {number=} options.authenticator.inactiveSessionLifespanSeconds session timeout
+   * @param {string[]=} options.authenticator.loginBlurb text for login page
+   * @param {string[]=} options.authenticator.indieAuthBlurb text for indieauth login section
+   * @param {string[]=} options.authenticator.userBlurb text for local user login section
+   * @param {string[]=} options.authenticator.otpBlurb text for otp entry
+   * @param {string=} options.dingus dingus options
+   * @param {string=} options.dingus.proxyPrefix base url prefix
    */
   constructor(logger, db, options) {
     this.logger = logger;
@@ -69,11 +79,11 @@ class Authenticator {
     };
     try {
       this.authn.argon2 = require('argon2');
-    } catch (e) { /**/ }
+    } catch (e) { /**/ } // eslint-disable-line no-unused-vars
     try {
       this.authn.pam = require('node-linux-pam');
       this.forbiddenPAMIdentifiers = options.authenticator.forbiddenPAMIdentifiers ?? ['root'];
-    } catch (e) { /**/ }
+    } catch (e) { /**/ } // eslint-disable-line no-unused-vars
 
     // Track which authn methods we can change credentials et cetera.
     const authnUpdatable = ['plain', 'argon2'];
@@ -107,10 +117,10 @@ class Authenticator {
   /**
    * Populate the authentication database with a new identifier, the
    * secured credential, and optionally an OTP key.
-   * @param {*} dbCtx
-   * @param {String} identifier
-   * @param {String} credential plaintext
-   * @param {String=} otpKey
+   * @param {*} dbCtx db context
+   * @param {string} identifier identifier
+   * @param {string} credential plaintext
+   * @param {string=} otpKey otp key
    * @returns {Promise<void>}
    */
   async createIdentifier(dbCtx, identifier, credential, otpKey = null) {
@@ -128,9 +138,9 @@ class Authenticator {
   /**
    * Update the authentication database with a new secured credential
    * for an indentifier.
-   * @param {*} dbCtx
-   * @param {*} identifier
-   * @param {*} credential plaintext
+   * @param {*} dbCtx dbCtx
+   * @param {string} identifier identifier
+   * @param {string} credential plaintext
    * @returns {Promise<void>}
    */
   async updateCredential(dbCtx, identifier, credential) {
@@ -148,9 +158,9 @@ class Authenticator {
 
   /**
    * Encode a plaintext credential in the preferred way to store in database.
-   * @param {String} credential
-   * @param {String=} authn
-   * @returns {Promise<String>}
+   * @param {string} credential plaintext
+   * @param {string=} authn authentication mechanism
+   * @returns {Promise<string>} encoded credential
    */
   async _secureCredential(credential, authn = this.authnPreferred) {
     const _scope = _fileScope('_secureCredential');
@@ -175,8 +185,8 @@ class Authenticator {
   /**
    * Checks a supplied credential against supplied data.
    * @param {AuthInfo} authData from database
-   * @param {String} credential plaintext
-   * @returns {Promise<Boolean>}
+   * @param {string} credential plaintext
+   * @returns {Promise<boolean>} is valid
    */
   async _validateAuthDataCredential(authData, credential) {
     const _scope = _fileScope('_validateAuthDataCredential');
@@ -204,9 +214,9 @@ class Authenticator {
 
   /**
    * Check argon2.
-   * @param {AuthInfo} authData
-   * @param {String} credential
-   * @returns {Promise<Boolean>}
+   * @param {AuthInfo} authData auth entry
+   * @param {string} credential to check
+   * @returns {Promise<boolean>} is valid
    */
   async _isValidArgon2Identifier(authData, credential) {
     return await this.authn.argon2.verify(authData.credential, credential);
@@ -215,9 +225,9 @@ class Authenticator {
 
   /**
    * Check plaintext.
-   * @param {AuthInfo} authData
-   * @param {String} credential
-   * @returns {Promise<Boolean>}
+   * @param {AuthInfo} authData auth entry
+   * @param {string} credential to check
+   * @returns {Promise<boolean>} is valid
    */
   static _isValidPlainIdentifier(authData, credential) {
     return authData.credential.substring('$plain$'.length) === credential;
@@ -226,9 +236,9 @@ class Authenticator {
 
   /**
    * Check system PAM.
-   * @param {AuthInfo} authData
-   * @param {String} credential
-   * @returns {Promise<Boolean>}
+   * @param {AuthInfo} authData auth entry
+   * @param {string} credential to check
+   * @returns {Promise<boolean>} is valid
    */
   async _isValidPAMIdentifier(authData, credential) {
     const _scope = _fileScope('_isValidPAMIdentifier');
@@ -254,10 +264,10 @@ class Authenticator {
    * Check local auth entries.
    * Sets ctx.authenticatedId if valid.
    * Sets ctx.otpKey if account has otpKey.
-   * @param {String} identifier
-   * @param {String} credential
-   * @param {Object} ctx
-   * @returns {Promise<Boolean>}
+   * @param {string} identifier identifier
+   * @param {string} credential to check
+   * @param {object} ctx context
+   * @returns {Promise<boolean>} is valid
    */
   async isValidIdentifierCredential(identifier, credential, ctx) {
     const _scope = _fileScope('isValidIdentifierCredential');
@@ -292,13 +302,16 @@ class Authenticator {
 
 
   /**
-   * 
-   * @param {OTPState} state
-   * @param {String} state.key
-   * @param {Number} state.attempt
-   * @param {Number} state.epochMs
-   * @param {String} otp
-   * @returns {String} Enum.OTPResult
+   * @typedef {object} OTPState
+   * @property {string} key otp key
+   * @property {number} attempt count of attempts
+   * @property {number} epochMs when entry was initiated
+   */
+  /**
+   * Validate if an entered otp token matches the key.
+   * @param {OTPState} state otp state
+   * @param {string} otp to check
+   * @returns {Enum.OTPResult} result
    */
   checkOTP(state, otp) {
     const totp = new this.TOTP({
@@ -319,16 +332,34 @@ class Authenticator {
   }
 
 
+  /**
+   * Update the authentication database with a new otp key.
+   * @param {*} dbCtx db context
+   * @param {string} identifier identifier
+   * @param {string=} otpKey otp key
+   */
+  async updateOTPKey(dbCtx, identifier, otpKey) {
+    const _scope = _fileScope('updateOTPKey');
+    try {
+      await this.db.authenticationUpdateOTPKey(dbCtx, identifier, otpKey);
+      this.logger.info(_scope, 'otp key updated');
+    } catch (e) {
+      this.logger.error(_scope, 'failed', { error: e, identifier });
+    }
+  }
+
+
   /**
    * Check for valid Basic auth, updates ctx with identifier if valid.
-   * @param {String} credentials
-   * @param {Object} ctx
-   * @returns {Promise<Boolean>}
+   * @param {string} authValue basic auth value (base64)
+   * @param {object} ctx context
+   * @returns {Promise<boolean>} is valid
    */
-  async isValidBasic(credentials, ctx) {
+  async isValidBasic(authValue, ctx) {
     const _scope = _fileScope('isValidBasic');
     this.logger.debug(_scope, 'called', { ctx });
 
+    const credentials = Buffer.from(authValue, 'base64').toString('utf-8');
     const [identifier, credential] = common.splitFirst(credentials, ':', '');
 
     return this.isValidIdentifierCredential(identifier, credential, ctx);
@@ -337,9 +368,9 @@ class Authenticator {
 
   /**
    * Determine which Authorization header is available, and if it is valid.
-   * @param {String} authorizationHeader
-   * @param {Object} ctx
-   * @returns {Promise<Boolean>}
+   * @param {string} authorizationHeader request header
+   * @param {object} ctx context
+   * @returns {Promise<boolean>} is valid
    */
   async isValidAuthorization(authorizationHeader, ctx) {
     const _scope = _fileScope('isValidAuthorization');
@@ -349,8 +380,7 @@ class Authenticator {
     // eslint-disable-next-line sonarjs/no-small-switch
     switch (authMethod.toLowerCase()) {
       case 'basic': {
-        const credentials = Buffer.from(authString, 'base64').toString('utf-8');
-        return this.isValidBasic(credentials, ctx);
+        return this.isValidBasic(authString, ctx);
       }
 
       default:
@@ -362,7 +392,7 @@ class Authenticator {
 
   /**
    * Send a response requesting basic auth.
-   * @param {http.ServerResponse} res
+   * @param {http.ServerResponse} res response
    */
   requestBasic(res) {
     res.setHeader(Enum.Header.WWWAuthenticate, `Basic realm="${this.basicRealm}", charset="UTF-8"`);
@@ -375,9 +405,9 @@ class Authenticator {
    * authenticated user.
    * Restores ctx.session from cookie data, sets ctx.authenticationId to
    * identifier or profile for session.
-   * @param {Object} ctx
-   * @param {Object} ctx.cookie
-   * @returns {Promise<Boolean>}
+   * @param {object} ctx context
+   * @param {object} ctx.cookie cookies object
+   * @returns {Promise<boolean>} is valid
    */
   async isValidCookieAuth(ctx) {
     const _scope = _fileScope('isValidCookieAuth');
@@ -416,13 +446,13 @@ class Authenticator {
    * @see sessionRequiredLocal
    * @see sessionOptional
    * @see sessionOptionalLocal
-   * @param {http.ClientRequest} req
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {String} loginPath
-   * @param {Boolean} required redirect to login url if no valid session
-   * @param {Boolean} profilesAllowed if true, an indieauth session is valid, otherwise only identifier/credential
-   * @returns {Promise<Boolean>}
+   * @param {http.ClientRequest} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {string} loginPath url path to redirect to when login is required
+   * @param {boolean} required redirect to login url if no valid session
+   * @param {boolean} profilesAllowed if true, an indieauth session is valid, otherwise only identifier/credential
+   * @returns {Promise<boolean>} is valid session
    */
   async sessionCheck(req, res, ctx, loginPath, required = true, profilesAllowed = true) {
     const _scope = _fileScope('sessionCheck');
@@ -474,11 +504,11 @@ class Authenticator {
 
   /**
    * Requires a valid session with a local identifier. Redirects to loginPath if not.
-   * @param {http.ClientRequest} req
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {String} loginPath
-   * @returns {Promise<Boolean>}
+   * @param {http.ClientRequest} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {string} loginPath url path to redirect to when login is needed
+   * @returns {Promise<boolean>} is valid session
    */
   async sessionRequiredLocal(req, res, ctx, loginPath) {
     return this.sessionCheck(req, res, ctx, loginPath, true, false);
@@ -487,11 +517,11 @@ class Authenticator {
 
   /**
    * Requires a valid session with either a local identifier or a profile. Redirects to loginPath if not.
-   * @param {http.ClientRequest} req
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {String} loginPath
-   * @returns {Promise<Boolean>}
+   * @param {http.ClientRequest} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {string} loginPath url path to redirect to when login is needed
+   * @returns {Promise<boolean>} is valid session
    */
   async sessionRequired(req, res, ctx, loginPath) {
     return this.sessionCheck(req, res, ctx, loginPath);
@@ -500,11 +530,10 @@ class Authenticator {
 
   /**
    * Check for a valid session with a local identifier, but do nothing if not.
-   * @param {http.ClientRequest} req
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {String} loginPath
-   * @returns {Promise<Boolean>}
+   * @param {http.ClientRequest} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @returns {Promise<boolean>} is valid session
    */
   async sessionOptionalLocal(req, res, ctx) {
     return this.sessionCheck(req, res, ctx, undefined, false, false);
@@ -513,11 +542,10 @@ class Authenticator {
 
   /**
    * Check for a valid session with either a local identifier or a profile, but do nothing if not.
-   * @param {http.ClientRequest} req
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {String} loginPath
-   * @returns {Promise<Boolean>}
+   * @param {http.ClientRequest} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @returns {Promise<boolean>} is valid session
    */
   async sessionOptional(req, res, ctx) {
     return this.sessionCheck(req, res, ctx, undefined, false);
@@ -529,27 +557,32 @@ class Authenticator {
    * Check for valid local identifier in Authorization header;
    * optionally fall back to session cookie if no header provided.
    * Prompts for Basic auth if not valid.
-   * @param {http.ClientRequest} req
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {Boolean} sessionAlsoValid
-   * @returns {Promise<Boolean}
+   * @param {http.ClientRequest} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {boolean} sessionAlsoValid fall back to session if no authz header
+   * @returns {Promise<boolean>} is valid
    */
   async apiRequiredLocal(req, res, ctx, sessionAlsoValid = true) {
     const _scope = _fileScope('apiRequiredLocal');
     this.logger.debug(_scope, 'called', { ctx, sessionAlsoValid });
 
-    // If a Authorization header was provided, never consider session as a fallback.
-    const authorizationHeader = req.getHeader(Enum.Header.Authorization);
-    if (authorizationHeader) {
-      if (await this.isValidAuthorization(authorizationHeader, ctx)) {
-        this.logger.debug(_scope, 'valid authorization', { ctx, sessionAlsoValid });
+    try {
+      // If a Authorization header was provided, never consider session as a fallback.
+      const authorizationHeader = req.getHeader(Enum.Header.Authorization);
+      if (authorizationHeader) {
+        if (await this.isValidAuthorization(authorizationHeader, ctx)) {
+          this.logger.debug(_scope, 'valid authorization', { ctx, sessionAlsoValid });
+          return true;
+        }
+      } else if (sessionAlsoValid
+      &&         await this.sessionCheck(req, res, ctx, undefined, false, false)) {
+        this.logger.debug(_scope, 'valid session', { ctx, sessionAlsoValid });
         return true;
       }
-    } else if (sessionAlsoValid
-    &&         await this.sessionCheck(req, res, ctx, undefined, false, false)) {
-      this.logger.debug(_scope, 'valid session', { ctx, sessionAlsoValid });
-      return true;
+    } catch (e) {
+      this.logger.error(_scope, 'failed', { error: e });
+      throw e;
     }
 
     this.logger.debug(_scope, 'invalid authorization', { ctx, sessionAlsoValid });
index 1c19aeb9dbde3172858ee3dcea9a00ceb1a8004e..5563254f0bab35b7b54322dd46ad0cfa9c49e3c4 100644 (file)
@@ -1,12 +1,15 @@
 'use strict';
 
 const { common } = require('@squeep/api-dingus');
+const { randomBytes } = require('node:crypto');
+const { promisify } = require('node:util');
 
+const randomBytesAsync = promisify(randomBytes);
 
 /**
  * Recursively freeze an object.
- * @param {Object} o
- * @returns {Object}
+ * @param {object} o object to freeze
+ * @returns {object} frozen object
  */
 const freezeDeep = (o) => {
   Object.freeze(o);
@@ -22,9 +25,9 @@ const freezeDeep = (o) => {
 
 /**
  * Return a new object duplicating `o`, without the properties specified.
- * @param {Object} o
- * @param {String[]} props
- * @returns {Object}
+ * @param {object} o source object
+ * @param {string[]} props list of property names to omit
+ * @returns {object} pruned object
  */
 const omit = (o, props) => {
   return Object.fromEntries(Object.entries(o).filter(([k]) => !props.includes(k)));
@@ -32,9 +35,10 @@ const omit = (o, props) => {
 
 /**
  * Helper to log mystery-box statistics.
- * @param {ConsoleLike} logger
- * @param {String} scope
- * @returns {Function}
+ * @param {object} logger logger
+ * @param {Function} logger.debug log debug
+ * @param {string} scope scope
+ * @returns {Function} stats log decorator
  */
 const mysteryBoxLogger = (logger, scope) => {
   return (s) => {
@@ -48,8 +52,8 @@ const mysteryBoxLogger = (logger, scope) => {
 
 /**
  * Hide sensitive part of an Authorization header.
- * @param {String} authHeader
- * @returns {String}
+ * @param {string} authHeader header value
+ * @returns {string} scrubbed header value
  */
 const obscureAuthorizationHeader = (authHeader) => {
   if (!authHeader) {
@@ -65,4 +69,5 @@ module.exports = Object.assign(Object.create(common), {
   mysteryBoxLogger,
   obscureAuthorizationHeader,
   omit,
-});
\ No newline at end of file
+  randomBytesAsync,
+});
index 98a6f182921185a8f0b4951b4d1439cb4606c76a..5f646ab31f1d7089ad209bbc2cb681bc1ae6b029 100644 (file)
@@ -2,6 +2,17 @@
 
 const { Errors } = require('@squeep/api-dingus');
 
+class ResourceAuthenticatorError extends Error {
+  constructor(...args) {
+    super(...args);
+    delete this.stack;
+  }
+  get name() {
+    return this.constructor.name;
+  }
+}
+
 module.exports = {
+  ResourceAuthenticatorError,
   ...Errors,
 };
\ No newline at end of file
diff --git a/lib/resource-authenticator.js b/lib/resource-authenticator.js
new file mode 100644 (file)
index 0000000..a635d9c
--- /dev/null
@@ -0,0 +1,223 @@
+'use strict';
+
+const { createHmac } = require('node:crypto');
+const uuid = require('uuid');
+const common = require('./common');
+const Enum = require('./enum');
+const { ResourceAuthenticatorError, ResponseError } = require('./errors');
+
+const _fileScope = common.fileScope(__filename);
+
+/**
+ * @typedef {import('node:http')} http
+ */
+
+/**
+ * @typedef {object} ResourceRecord
+ * @property {string} secret secret
+ */
+
+/**
+ * @typedef {object} ConsoleLike
+ * @property {Function} debug log debug
+ * @property {Function} error log error
+ * @property {Function} info log info
+ */
+
+/**
+ * @callback DBContextExec
+ * @param {object} dbCtx db context
+ * @returns {Promise<any>} result
+ */
+
+/**
+ * @typedef {object} ResourceDBInterface
+ * @property {(DBContextExec) => Promise<any>} context db context runner
+ * @property {(dbCtx: any, identifier: string) => Promise<ResourceRecord>} resourceGet get a resource entry
+ */
+
+/**
+ * @typedef {object} ResourceAuthenticatorOptions
+ * @property {string=} digestAlgorithm digest algorithm
+ * @property {number=} graceSeconds grace seconds, give or take
+ * @property {number=} saltBytes salt bytes
+ */
+
+/**
+ * @type {ResourceAuthenticatorOptions}
+ */
+const defaultResourceAuthenticatorOptions = {
+  digestAlgorithm: 'sha256',
+  graceSeconds: 60,
+  saltBytes: 21,
+};
+
+class ResourceAuthenticator {
+  /**
+   * 
+   * @param {ConsoleLike} logger logger instance
+   * @param {ResourceDBInterface} db db instance
+   * @param {object} options options
+   * @param {ResourceAuthenticatorOptions} options.resourceAuthenticator resource authenticator options
+   */
+  constructor(logger, db, options = {}) {
+    this.logger = logger;
+    this.db = db;
+    common.setOptions(this, defaultResourceAuthenticatorOptions, options?.resourceAuthenticator || {});
+  }
+
+
+  /**
+   * Get the current epoch.
+   * @returns {number} epoch seconds
+   */
+  static get currentEpoch() {
+    return Math.ceil(Date.now() / 1000);
+  }
+
+
+  /**
+   * Compact uuid identifier as 24 character url-safe string.
+   * @param {string} identifier uuid
+   * @returns {string} base64url encoded uuid
+   */
+  static ensmallenIdentifier(identifier) {
+    return Buffer.from(uuid.parse(identifier)).toString('base64url');
+  }
+
+
+  /**
+   * Expand compacted uuid identifier.
+   * @param {string} compact base64url encoded uuid
+   * @returns {string} uuid
+   */
+  static embiggenIdentifier(compact) {
+    const uuidBuffer = Buffer.from(compact, 'base64url');
+    return uuid.stringify(uuidBuffer);
+  }
+
+
+  /**
+   * Get an encoded salt string.
+   * @param {number} saltBytes salt bytes
+   * @returns {Promise<string>} base64url encoded
+   */
+  async getSalt(saltBytes = this.saltBytes) {
+    const saltBuffer = await common.randomBytesAsync(saltBytes);
+    return saltBuffer.toString('base64url');
+  }
+
+
+  /**
+   * Smoosh some contents through the configured HMAC.
+   * @param {Buffer|string} secret secret
+   * @param  {...any} contents contents
+   * @returns {string} base64url encoded digest
+   */
+  createDigest(secret, ...contents) {
+    const hmac = createHmac(this.digestAlgorithm, secret);
+    contents.forEach((content) => hmac.update(Buffer.from(content)));
+    return hmac.digest('base64url');
+  }
+
+  /**
+   * Return an authorization header value for an identifier and secret.
+   * @param {string} identifier uuid
+   * @param {string} secret secret
+   * @returns {Promise<string>} authorization header
+   */
+  async authenticate(identifier, secret) {
+    const authenticationType = 'Bearer';
+    const currentEpoch = this.constructor.currentEpoch.toString();
+    const smallIdentifier = this.constructor.ensmallenIdentifier(identifier);
+    const salt = await this.getSalt();
+    const token = [
+      smallIdentifier,
+      currentEpoch,
+      salt,
+      this.createDigest(secret, smallIdentifier, currentEpoch, salt),
+    ].join(':');
+    return [authenticationType, token].join(' ');
+  }
+
+
+  /**
+   * Require a request to include a valid resource bearer token.
+   * Resource bearer tokens are of the form 'compact-identifier:epoch:salt:${hmac(identifier + epoch + salt)}'
+   * where the hmac using a pre-shared secret.
+   * Sets ctx.resourceIdentifier on success.
+   * @param {http.IncomingMessage} req request
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @returns {Promise<void>}
+   */
+  async required(req, res, ctx) {
+    const _scope = _fileScope('required');
+    this.logger.debug(_scope, 'called');
+
+    try {
+      const authHeader = req.getHeader(Enum.Header.Authorization);
+      if (!authHeader) {
+        throw new ResourceAuthenticatorError('no authz header');
+      }
+
+      const [ authMethod, credentials ] = common.splitFirst(authHeader, ' ', '');
+      if (authMethod.toLowerCase() !== 'bearer' || !credentials) {
+        throw new ResourceAuthenticatorError('authz header not bearer type');
+      }
+
+      const [tIdentifier, tEpoch, tSalt, tDigest, ...tRest] = credentials.split(':');
+      if (!tIdentifier || !tEpoch || !tSalt || !tDigest) {
+        throw new ResourceAuthenticatorError('unparsable bearer token');
+      }
+      if (tRest.length) {
+        this.logger.debug(_scope, 'unexpected additional fields in bearer token', { tRest });
+      }
+
+      const identifier = this.constructor.embiggenIdentifier(tIdentifier);
+
+      const resource = await this.db.context(async (dbCtx) => {
+        return await this.db.resourceGet(dbCtx, identifier);
+      }); // dbCtx
+      if (!resource) {
+        throw new ResourceAuthenticatorError('invalid resource');
+      }
+
+      const resourceEpoch = parseInt(tEpoch, 10);
+      const epochDrift = Math.abs(resourceEpoch - this.constructor.currentEpoch);
+      if (epochDrift > this.graceSeconds) {
+        throw new ResourceAuthenticatorError('timestamp out of grace period');
+      }
+
+      const digest = this.createDigest(resource.secret, tIdentifier, tEpoch, tSalt);
+      if (digest !== tDigest) {
+        throw new ResourceAuthenticatorError('invalid digest');
+      }
+
+      // Success
+      ctx.resourceIdentifier = identifier;
+      this.logger.debug(_scope, 'success');
+    } catch (e) {
+      if (e instanceof ResourceAuthenticatorError) {
+        this.logger.debug(_scope, 'invalid', { error: e });
+        return this.constructor.requestBearer(res);
+      }
+      this.logger.error(_scope, 'failed', { error: e });
+      throw e;
+    }
+  }
+
+
+  /**
+   * Return a status code requesting bearer authentication.
+   * @param {http.ServerResponse} res response
+   * @param {object} response defaults to Unauthorized status
+   */
+  static requestBearer(res, response = Enum.ErrorResponse.Unauthorized) {
+    res.setHeader(Enum.Header.WWWAuthenticate, 'Bearer');
+    throw new ResponseError(response);
+  }
+
+}
+
+module.exports = ResourceAuthenticator;
\ No newline at end of file
index 2d3d96cd10706e07512ee3d908a6d849adb829b5..d399e5bd1fb480044459cde001e7eda8a90f213b 100644 (file)
@@ -8,27 +8,38 @@
 const { Communication: IndieAuthCommunication } = require('@squeep/indieauth-helper');
 const { MysteryBox } = require('@squeep/mystery-box');
 const { TOTP } = require('@squeep/totp');
-const { randomUUID } = require('crypto');
+const { randomUUID } = require('node:crypto');
 const common = require('./common');
 const Enum = require('./enum');
 const Template = require('./template');
 
 const _fileScope = common.fileScope(__filename);
 
+/**
+ * @typedef {import('node:http')} http
+ * @typedef {import('./authenticator')} Authenticator
+ */
+/**
+ * @typedef {object} ConsoleLike
+ * @property {Function} debug log debug
+ * @property {Function} error log error
+ * @property {Function} info log info
+ */
+
 class SessionManager {
   /**
-   * @param {Console} logger
-   * @param {Authenticator} authenticator
-   * @param {Object} options
-   * @param {Object} options.authenticator
-   * @param {String[]} options.authenticator.authnEnabled
-   * @param {Number=} options.authenticator.inactiveSessionLifespanSeconds
-   * @param {Boolean} options.authenticator.secureAuthOnly
-   * @param {Object=} options.dingus
-   * @param {String=} options.dingus.proxyPrefix
-   * @param {String} options.dingus.selfBaseUrl
-   * @param {Object} options.manager
-   * @param {String} options.manager.pageTitle
+   * @param {ConsoleLike} logger logger
+   * @param {Authenticator} authenticator authenticator instance
+   * @param {object} options options
+   * @param {object} options.authenticator authenticator instance options
+   * @param {string[]} options.authenticator.authnEnabled authentication methods enabled
+   * @param {number=} options.authenticator.inactiveSessionLifespanSeconds session timeout
+   * @param {boolean} options.authenticator.secureAuthOnly allow only https
+   * @param {object=} options.dingus dingus options
+   * @param {string=} options.dingus.proxyPrefix prefix on route paths
+   * @param {string} options.dingus.selfBaseUrl base url
+   * @param {object} options.manager manager options
+   * @param {string} options.manager.pageTitle page title
    */
   constructor(logger, authenticator, options) {
     this.logger = logger;
@@ -46,10 +57,10 @@ class SessionManager {
 
   /**
    * Set or update our session cookie.
-   * @param {http.ServerResponse} res
-   * @param {Object=} session
-   * @param {Number=} maxAge
-   * @param {String=} path
+   * @param {http.ServerResponse} res respoonse
+   * @param {object=} session session object
+   * @param {number=} maxAge session validity in seconds
+   * @param {string=} path session cookie path
    */
   async _sessionCookieSet(res, session, maxAge = this.cookieLifespan, path = '/') {
     const cookieName = Enum.SessionCookie;
@@ -66,22 +77,22 @@ class SessionManager {
 
   /**
    * Remove any current session cookie.
-   * @param {http.ServerResponse} res
-   * @param {String} path
+   * @param {http.ServerResponse} res response
+   * @param {string} path session cookie path
    */
   async _sessionCookieClear(res, path = '/') {
     await this._sessionCookieSet(res, undefined, 0, path);
   }
 
   /**
-   * @typedef {(pagePathLevel: Number, ctx: Object, htmlOptions: Object) => void} AppTemplateCallback
+   * @typedef {(pagePathLevel: number, ctx: object, htmlOptions: object) => void} AppTemplateCallback
    */
 
   /**
    * GET request for establishing admin session.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {AppTemplateCallback} appCb
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
    */
   async getAdminLogin(res, ctx, appCb) {
     const _scope = _fileScope('getAdminLogin');
@@ -108,9 +119,9 @@ class SessionManager {
 
   /**
    * POST request for taking form data to establish admin session.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {AppTemplateCallback} appCb
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
    */
   async postAdminLogin(res, ctx, appCb) {
     const _scope = _fileScope('postAdminLogin');
@@ -136,7 +147,7 @@ class SessionManager {
     try {
       me = new URL(ctx.parsedBody['me']);
       meAutoScheme = !!ctx.parsedBody['me_auto_scheme'];
-    } catch (e) {
+    } catch (e) { // eslint-disable-line no-unused-vars
       this.logger.debug(_scope, 'failed to parse supplied profile url', { ctx });
       ctx.errors.push(`Unable to understand '${ctx.parsedBody['me']}' as a profile URL.`);
     }
@@ -161,7 +172,7 @@ class SessionManager {
       // fetch and parse me for 'authorization_endpoint' relation links
       try {
         authorizationEndpoint = new URL(profile.metadata.authorizationEndpoint);
-      } catch (e) {
+      } catch (e) { // eslint-disable-line no-unused-vars
         ctx.errors.push(`Unable to understand the authorization endpoint ('${profile.metadata.authorizationEndpoint}') indicated by that profile ('${me}') as a URL.`);
       }
 
@@ -175,7 +186,7 @@ class SessionManager {
             this.logger.debug(_scope, 'supplied issuer url invalid', { ctx });
             ctx.errors.push('Authorization server provided an invalid issuer field.');
           }
-        } catch (e) {
+        } catch (e) { // eslint-disable-line no-unused-vars
           this.logger.debug(_scope, 'failed to parse supplied issuer url', { ctx });
           ctx.errors.push('Authorization server provided an unparsable issuer field.');
         }
@@ -227,15 +238,15 @@ class SessionManager {
 
 
   /**
-   * @typedef {Object} OTPState
-   * @property {String} authenticatedIdentifier
-   * @property {Buffer|String} key
-   * @property {Number} attempt
-   * @property {Number} epochMs
-   * @property {String} redirect
+   * @typedef {object} OTPState
+   * @property {string} authenticatedIdentifier identifier of logging-in user
+   * @property {Buffer | string} key otp key
+   * @property {number} attempt counter
+   * @property {number} epochMs started
+   * @property {string} redirect where to go after successful otp entry
    */
   /**
-   * @param {OTPState} otpState
+   * @param {OTPState} otpState otp state
    */
   static _validateOTPState(otpState) {
     if (!otpState.authenticatedIdentifier) {
@@ -260,12 +271,13 @@ class SessionManager {
    * Check if processing an OTP entry attempt.  If not, resume login flow.
    * If so, validate otp and establish session, else reprompt for OTP, or
    * return to login entry after too many failures.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {Object} ctx.parsedBody
-   * @param {String} ctx.parsedBody.state
-   * @param {String} ctx.parsedBody.otp
-   * @returns {Promise<Boolean>} true if otp was handled, otherwise false indicates further login processing needed
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {object} ctx.parsedBody submitted data
+   * @param {string} ctx.parsedBody.state packed state
+   * @param {string} ctx.parsedBody.otp entered code
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
+   * @returns {Promise<boolean>} true if otp was handled, otherwise false indicates further login processing needed
    */
   async _otpSubmission(res, ctx, appCb) {
     const _scope = _fileScope('_otpSubmission');
@@ -279,7 +291,7 @@ class SessionManager {
       // Ignore and continue back to main login.
       return false;
     }
-    /** @type OTPState */
+    /** @type {OTPState} */
     let state;
     try {
       state = await this.mysteryBox.unpack(stateBox);
@@ -337,9 +349,10 @@ class SessionManager {
 
   /**
    * 
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @returns {Promise<Boolean>} true if handled, false if flow should continue
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
+   * @returns {Promise<boolean>} true if handled, false if flow should continue
    */
   async _localUserAuth(res, ctx, appCb) {
     const _scope = _fileScope('_localUserAuth');
@@ -394,8 +407,8 @@ class SessionManager {
 
   /**
    * GET request to remove current credentials.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
    */
   async getAdminLogout(res, ctx) {
     const _scope = _fileScope('getAdminLogout');
@@ -416,9 +429,9 @@ class SessionManager {
   /**
    * GET request for returning IndieAuth redirect.
    * This currently only redeems a scope-less profile.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {AppTemplateCallback} appCb
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
    */
   async getAdminIA(res, ctx, appCb) {
     const _scope = _fileScope('getAdminIA');
@@ -438,7 +451,7 @@ class SessionManager {
         ctx.session = await this.mysteryBox.unpack(cookieValue);
         this.logger.debug(_scope, 'restored session from cookie', { ctx });
       } catch (e) {
-        this.logger.debug(_scope, 'could not unpack cookie');
+        this.logger.debug(_scope, 'could not unpack cookie', { error: e });
         ctx.errors.push('invalid cookie');
       }
     }
@@ -481,7 +494,7 @@ class SessionManager {
     let redeemProfileUrl;
     try {
       redeemProfileUrl = new URL(ctx.session.authorizationEndpoint);
-    } catch (e) {
+    } catch (e) { // eslint-disable-line no-unused-vars
       this.logger.debug(_scope, 'failed to parse restored session authorization endpoint as url', { ctx });
       ctx.errors.push('invalid cookie');
     }
@@ -531,11 +544,17 @@ class SessionManager {
   }
 
 
+  /**
+   * @typedef {object} AuthInfo
+   * @property {string} identifier identifier
+   * @property {string} credential hashed credential
+   * @property {string=} otpKey otp key
+   */
   /**
    * Page for modifying credentials and OTP.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {AppTemplateCallback} appCb
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
    */
   async getAdminSettings(res, ctx, appCb) {
     const _scope = _fileScope('getAdminSettings');
@@ -562,10 +581,9 @@ class SessionManager {
 
   /**
    * Page for modifying credentials and OTP.
-   * @param {http.ServerResponse} res
-   * @param {Object} ctx
-   * @param {Object[]=} appNavLinks
-   * @param {AppTemplateCallback} appCb
+   * @param {http.ServerResponse} res response
+   * @param {object} ctx context
+   * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
    */
   async postAdminSettings(res, ctx, appCb) {
     const _scope = _fileScope('postAdminSettings');
@@ -586,7 +604,7 @@ class SessionManager {
             await this._otpDisable(dbCtx, ctx, authData);
             return;
 
-          case 'confirm': 
+          case 'confirm':
             await this._otpConfirm(dbCtx, ctx);
             return;
 
@@ -614,9 +632,9 @@ class SessionManager {
 
   /**
    * Submission to disable OTP.
-   * @param {*} dbCtx 
-   * @param {*} ctx 
-   * @param {AuthInfo} authData 
+   * @param {*} dbCtx db context
+   * @param {*} ctx context
+   * @param {AuthInfo} authData auth info
    */
   async _otpDisable(dbCtx, ctx, authData) {
     const _scope = _fileScope('_otpDisable');
@@ -635,7 +653,7 @@ class SessionManager {
 
   /**
    * Submission to enable OTP.
-   * @param {Object} ctx 
+   * @param {object} ctx context
    */
   async _otpEnable(ctx) {
     const _scope = _fileScope('_otpEnable');
@@ -657,8 +675,8 @@ class SessionManager {
 
   /**
    * Submission to confirm enabling OTP.
-   * @param {*} dbCtx 
-   * @param {Object} ctx 
+   * @param {*} dbCtx  db context
+   * @param {object} ctx context
    */
   async _otpConfirm(dbCtx, ctx) {
     const _scope = _fileScope('_otpConfirm');
@@ -715,9 +733,9 @@ class SessionManager {
 
   /**
    * Submission to set new credential.
-   * @param {*} dbCtx 
-   * @param {Object} ctx 
-   * @param {AuthInfo} authData 
+   * @param {*} dbCtx db context
+   * @param {object} ctx context
+   * @param {AuthInfo} authData auth info
    */
   async _credentialUpdate(dbCtx, ctx, authData) {
     const _scope = _fileScope('_credentialUpdate');
index 5e7632e9b7f116f2dcd6d74f56be51b685de21d7..c3a0448eb21e6637e755c833e32efdece36e9a9e 100644 (file)
@@ -5,8 +5,8 @@ const stream = require('node:stream');
 
 /**
  * Read a credential from stdin in a silent manner.
- * @param {String} prompt
- * @returns {Promise<String>}
+ * @param {string} prompt prompt
+ * @returns {Promise<string>} input credential
  */
 async function stdioCredential(prompt) {
   const input = process.stdin;
@@ -33,4 +33,4 @@ async function stdioCredential(prompt) {
   });
 }
 
-module.exports = stdioCredential;
\ No newline at end of file
+module.exports = stdioCredential;
index 67162f867f8ae784e215b43a829ed5c99b05fd70..8adeb206a04b47d8db34d8f90c9e795f0dc64372 100644 (file)
@@ -2,15 +2,15 @@
 
 /**
  * Populates nevLinks with (currently hardcoded) session-related links.
- * @param {Number} pagePathLevel relative to base
- * @param {Object} ctx
- * @param {String=} ctx.url redirect on logout
- * @param {Object=} ctx.session
- * @param {String=} ctx.session.authenticatedIdentifier
- * @param {String=} ctx.session.authenticatedProfile
- * @param {Object} options
- * @param {Object[]=} options.navLinks created if not present
- * @param {String=} options.pageIdentifier
+ * @param {number} pagePathLevel relative to base
+ * @param {object} ctx context
+ * @param {string=} ctx.url redirect on logout
+ * @param {object=} ctx.session session object
+ * @param {string=} ctx.session.authenticatedIdentifier active authentication identifier
+ * @param {string=} ctx.session.authenticatedProfile active indieauth profile
+ * @param {object} options options
+ * @param {object[]=} options.navLinks created if not present
+ * @param {string=} options.pageIdentifier internal reference to page template being rendered
  */
 function sessionNavLinks(pagePathLevel, ctx, options) {
   if (!options.navLinks) {
index 8a27479eb8426e4a3df658f9b71d382414754556..d3508e1466ce01c54e71833b9749c0cfdcd0d021 100644 (file)
@@ -3,9 +3,21 @@
 const { TemplateHelper: th } = require('@squeep/html-template-helper');
 const { sessionNavLinks } = require('./helpers');
 
+/**
+ * @alias {object} Context
+ */
+
+/**
+ * @alias {object} HtmlOptions
+ */
+
+/**
+ * @typedef {import('../session-manager').AppTemplateCallback} AppTemplateCallback
+ */
+
 /**
  *
- * @returns {String}
+ * @returns {string[]} section
  */
 function mainContent() {
   return [
@@ -15,17 +27,17 @@ function mainContent() {
   ];
 }
 
+
 /**
  * Render any errors from attempting IndieAuth.
- * @param {Object} ctx
- * @param {String[]} ctx.errors
- * @param {Object} options
- * @param {Object} options.manager
- * @param {String} options.manager.pageTitle
- * @param {Object} options.dingus
- * @param {String} options.dingus.selfBaseUrl
- * @param {(pagePathLevel, ctx, htmlOptions) => {void}} appCb
- * @returns {String}
+ * @param {Context} ctx context
+ * @param {object} options options
+ * @param {object} options.manager manager options
+ * @param {string} options.manager.pageTitle page title
+ * @param {object} options.dingus dingus options
+ * @param {string} options.dingus.selfBaseUrl base url
+ * @param {AppTemplateCallback} appCb function to mogrify htmlOptions
+ * @returns {string} page
  */
 module.exports = (ctx, options, appCb = () => {}) => {
   const pagePathLevel = 1;
index 8ee7392ad9f4883150546a444f1f4c7c23a2c42f..c02a624e8b4a248c0e02ae6ff75c2e393e47b782 100644 (file)
@@ -4,7 +4,23 @@ const { TemplateHelper: th } = require('@squeep/html-template-helper');
 const { sessionNavLinks } = require('./helpers');
 
 /**
- * Login form.
+ * @typedef {object} Context
+ * @property {string} clientProtocol http/https
+ */
+
+/**
+ * @typedef {object} HtmlOptions
+ * @property {string[]=} authnEnabled list of active authn methods
+ * @property {string} indieAuthBlurb content accompanying login fields
+ * @property {boolean} secureAuthOnly do not display user logiin if insecure and not allowed
+ * @property {string} userBlurb content accompanying login fields
+ */
+
+/**
+ * IndieAuth Profile login form.
+ * @param {Context} ctx context
+ * @param {HtmlOptions} options options
+ * @returns {string} section
  */
 function indieAuthSection(ctx, options) {
   const indieAuthBlurb = (options.indieAuthBlurb || []).map((x) => '\t'.repeat(6) + x).join('\n');
@@ -30,6 +46,9 @@ ${indieAuthBlurb}
  * Default all URL inputs to https if scheme not specified,
  * and set a flag if that happened.
  * From https://aaronparecki.com/2019/05/13/2/https
+ * @param {Context} ctx context
+ * @param {HtmlOptions} options options
+ * @returns {string} script
  */
 function indieAuthURLTrySecureFirstScript(ctx, options) {
   const showIndieAuthForm = options.authnEnabled.includes('indieAuth');
@@ -64,7 +83,16 @@ document.addEventListener('DOMContentLoaded', function() {
 </script>` : '';
 }
 
+/**
+ * Display user section when any of these methods are active.
+ */
 const userAuthn = ['argon2', 'pam'];
+/**
+ * Local identifier/credential login form.
+ * @param {Context} ctx context
+ * @param {HtmlOptions} options options
+ * @returns {string} section
+ */
 function userSection(ctx, options) {
   const userBlurb = (options.userBlurb || []).map((x) => '\t'.repeat(6) + x).join('\n');
   const secure = (ctx.clientProtocol || '').toLowerCase() === 'https';
@@ -88,27 +116,32 @@ ${userBlurb}
     : '';
 }
 
+/**
+ * @typedef {import('../session-manager').AppTemplateCallback} AppTemplateCallback
+ */
 
 /**
  * Render login form for both local and profile authentication.
- * @param {Object} ctx
- * @param {String[]=} ctx.errors
- * @param {String} ctx.clientProtocol
- * @param {Object} options
- * @param {Boolean} options.authenticator.secureAuthOnly
- * @param {String[]=} options.authenticator.loginBlurb
- * @param {String[]=} options.authenticator.indieAuthBlurb
- * @param {String[]=} options.authenticator.userBlurb
- * @param {Object} options.manager
- * @param {String} options.manager.pageTitle
- * @param {String=} options.manager.logoUrl
- * @param {Object} options.dingus
- * @param {String} options.dingus.selfBaseUrl
- * @param {() => {}} appCb
- * @returns {String}
+ * @param {Context} ctx context
+ * @param {object} options options
+ * @param {boolean} options.authenticator.secureAuthOnly do not display user login if not secure or allowed
+ * @param {string[]=} options.authenticator.loginBlurb content included at top of page
+ * @param {string} options.authenticator.indieAuthBlurb content included with indieauth login
+ * @param {string} options.authenticator.userBlurb content included with local user login
+ * @param {object} options.manager manager options
+ * @param {string} options.manager.pageTitle page title
+ * @param {string=} options.manager.logoUrl url
+ * @param {string=} options.manager.logoAlt alt for logo
+ * @param {object} options.dingus dingus options
+ * @param {string} options.dingus.selfBaseUrl root url
+ * @param {AppTemplateCallback} appCb function to mogrify template htmlOptions
+ * @returns {string} page
  */
 module.exports = (ctx, options, appCb = () => {}) => {
   const pagePathLevel = 1;
+  /**
+   * @type {HtmlOptions}
+   */
   const htmlOptions = {
     pageIdentifier: 'login',
     pageTitle: options.manager.pageTitle,
index 389f68670964b4a4b6fbb9daa45246fa4168d42a..5777d3981219ad1ba851e7001f98b8b3b3bfe4ae 100644 (file)
@@ -3,10 +3,31 @@
 const { TemplateHelper: th } = require('@squeep/html-template-helper');
 const { sessionNavLinks } = require('./helpers');
 
+/**
+ * @typedef {object} Context
+ * @property {string[]=} otpBlurb content accompanying otp entry
+ * @property {string} otpState packed otp state
+ */
+
+/**
+ * @alias {object} HtmlOptions
+ */
+
+/**
+ * @typedef {import('../session-manager').AppTemplateCallback} AppTemplateCallback
+ */
+
+
 /**
  * Login form, continued.
  */
 
+/**
+ *
+ * @param {Context} ctx context
+ * @param {HtmlOptions} options htmlOptions
+ * @returns {string} section
+ */
 function otpSection(ctx, options) {
   const otpBlurb = (options.otpBlurb || []).map((x) => '\t'.repeat(6) + x).join('\n');
   return `\t\t\t<section class="otp">
@@ -27,19 +48,17 @@ ${otpBlurb}
 
 /**
  * Render 2fs form.
- * @param {Object} ctx
- * @param {String[]=} ctx.errors
- * @param {String} ctx.otpState
- * @param {Object} options
- * @param {String[]=} options.authenticator.otpBlurb
- * @param {String[]=} options.authenticator.loginBlurb
- * @param {Object} options.manager
- * @param {String} options.manager.pageTitle
- * @param {String=} options.manager.logoUrl
- * @param {Object} options.dingus
- * @param {String} options.dingus.selfBaseUrl
- * @param {() => {}} appCb
- * @returns {String}
+ * @param {Context} ctx context
+ * @param {object} options options
+ * @param {string[]=} options.authenticator.otpBlurb content accompanying otp entry
+ * @param {object} options.manager manager options
+ * @param {string} options.manager.pageTitle page title
+ * @param {string=} options.manager.logoUrl logo url
+ * @param {string=} options.manager.logoAlt logo alt text
+ * @param {object} options.dingus dingus options
+ * @param {string} options.dingus.selfBaseUrl root url
+ * @param {AppTemplateCallback} appCb function to mogrify htmlOptions
+ * @returns {string} page
  */
 module.exports = (ctx, options, appCb = () => {}) => {
   const pagePathLevel = 1;
index 42bfdcda4fedae3e6c12a25177cb87943f41bfbc..aba75f921126dbb42682bd77adf5f43df2b976ed 100644 (file)
@@ -6,6 +6,27 @@ const { TemplateHelper: th } = require('@squeep/html-template-helper');
 const { sessionNavLinks } = require('./helpers');
 const { TOTP } = require('@squeep/totp');
 
+/**
+ * @typedef {object} Context
+ * @property {string=} otpConfirmBox encrypted otp state
+ * @property {string=} otpConfirmKey otp key to confirm adding to identifier
+ * @property {string=} authenticationId identifier
+ */
+
+/**
+ * @alias {object} HtmlOptions
+ */
+
+/**
+ * @typedef {import('../session-manager').AppTemplateCallback} AppTemplateCallback
+ */
+
+/**
+ * Render password update section.
+ * @param {Context} ctx context
+ * @param {HtmlOptions} htmlOptions options
+ * @returns {string} section
+ */
 function updatePasswordSection(ctx, htmlOptions) {
   return `\t\t\t<section class="settings-update-password">
 \t\t\t\t<h2>Password</h2>
@@ -28,6 +49,12 @@ function updatePasswordSection(ctx, htmlOptions) {
 }
 
 
+/**
+ *
+ * @param {Context} ctx context
+ * @param {HtmlOptions} htmlOptions options
+ * @returns {string} otp enable section
+ */
 function enableOTPSection(ctx, htmlOptions) {
   return `\t\t\t<section class="settings-otp">
 \t\t\t\t<h2>OTP 2FA</h2>
@@ -41,6 +68,12 @@ function enableOTPSection(ctx, htmlOptions) {
 }
 
 
+/**
+ *
+ * @param {Context} ctx context
+ * @param {htmlOptions} htmlOptions options
+ * @returns {string} otp confirm section
+ */
 function confirmOTPSection(ctx, htmlOptions) {
   const { secret, svg, uri } = TOTP.createKeySVG({
     accountname: ctx.authenticationId,
@@ -74,6 +107,12 @@ ${svg}
 }
 
 
+/**
+ *
+ * @param {Context} ctx context
+ * @param {HtmlOptions} htmlOptions options
+ * @returns {string} disable otp section
+ */
 function disableOTPSection(ctx, htmlOptions) {
   return `\t\t\t<section class="settings-otp">
 \t\t\t\t<h2>OTP 2FA</h2>
@@ -85,6 +124,12 @@ function disableOTPSection(ctx, htmlOptions) {
 }
 
 
+/**
+ *
+ * @param {Context} ctx context
+ * @param {HtmlOptions} htmlOptions options
+ * @returns {string} otp section
+ */
 function OTPSection(ctx, htmlOptions) {
   const OTPToggle = ctx.otpKey ? disableOTPSection : enableOTPSection;
   const OTPContent = ctx.otpConfirmBox ? confirmOTPSection : OTPToggle;
@@ -93,7 +138,13 @@ function OTPSection(ctx, htmlOptions) {
     '\t\t\t</section>';
 }
 
-
+/**
+ * Render settings form for managing credential and otp key.
+ * @param {Context} ctx context
+ * @param {object} options options
+ * @param {AppTemplateCallback} appCb function to mogrify htmlOptions
+ * @returns {string} page
+ */
 module.exports = (ctx, options, appCb = () => {}) => {
   const pagePathLevel = 1;
   const htmlOptions = {
index 977ca4a27022e1487f3ab494cd91ec08e6f94d90..d35a87ea3edd0ff76000a5fe91f9a28ab23849ac 100644 (file)
@@ -1,49 +1,38 @@
 {
   "name": "@squeep/authentication-module",
-  "version": "1.3.2",
-  "lockfileVersion": 2,
+  "version": "1.5.0",
+  "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@squeep/authentication-module",
-      "version": "1.3.2",
+      "version": "1.5.0",
       "license": "ISC",
       "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"
       },
       "devDependencies": {
-        "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.24.0",
-        "html-validate": "^8.15.0",
-        "mocha": "^10.3.0",
-        "nyc": "^15.1.0",
-        "pre-commit": "^1.2.2",
-        "sinon": "^17.0.1"
+        "@squeep/eslint-config": "^1",
+        "eslint": "^9",
+        "html-validate": "^8",
+        "mocha": "^10",
+        "nyc": "^15",
+        "pre-commit": "^1",
+        "sinon": "^17"
       },
       "engines": {
-        "node": "^18"
+        "node": ">=18"
       },
       "optionalDependencies": {
         "argon2": "^0.40.1",
         "node-linux-pam": "^0.2.1"
       }
     },
-    "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_modules/@ampproject/remapping": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz",
-      "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
+      "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.24.3",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz",
-      "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==",
+      "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.1",
+        "@babel/generator": "^7.24.5",
         "@babel/helper-compilation-targets": "^7.23.6",
-        "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.24.1",
-        "@babel/parser": "^7.24.1",
+        "@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",
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
-      "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==",
+      "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"
       }
     },
     "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"
       }
     },
     "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"
       }
     },
     "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"
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz",
-      "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==",
+      "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"
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
-      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
+      "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"
       }
     },
     "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"
       },
       }
     },
     "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.42.0",
+      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz",
+      "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==",
+      "dev": true,
+      "dependencies": {
+        "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",
         "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",
       }
     },
     "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",
         "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": {
       }
     },
     "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"
       },
       }
     },
     "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
-      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+      "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",
       }
     },
     "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"
       "dev": true
     },
     "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/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/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/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"
       }
     },
     "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"
       }
       "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
       "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="
     },
-    "node_modules/@ungap/structured-clone": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
-      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
-      "dev": true
-    },
     "node_modules/abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
       "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",
         "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"
-      }
-    },
     "node_modules/cacheable-lookup": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
         "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",
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001600",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
-      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
+      "version": "1.0.30001616",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz",
+      "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==",
       "dev": true,
       "funding": [
         {
       "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",
         "node": ">=0.3.1"
       }
     },
-    "node_modules/doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
-      "dev": true,
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
     "node_modules/eastasianwidth": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
       "dev": true
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.715",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
-      "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==",
+      "version": "1.4.757",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.757.tgz",
+      "integrity": "sha512-jftDaCknYSSt/+KKeXzH3LX5E2CvRLm75P3Hj+J/dv3CL0qUYcOt13d5FN1NiL5IJbbhzHrb3BomeG2tkSlZmw==",
       "dev": true
     },
     "node_modules/emoji-regex": {
       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
       "dev": true
     },
+    "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",
       }
     },
     "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",
         "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"
         "eslint": ">=8"
       }
     },
+    "node_modules/eslint-plugin-jsdoc": {
+      "version": "48.2.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.3.tgz",
+      "integrity": "sha512-r9DMAmFs66VNvNqRLLjHejdnJtILrt3xGi+Qx0op0oRfFGVpOR1Hb3BC++MacseHx93d8SKYPhyrC9BS7Os2QA==",
+      "dev": true,
+      "dependencies": {
+        "@es-joy/jsdoccomment": "~0.42.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.5.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.5.1.tgz",
+      "integrity": "sha512-+E242KoY16xtwqqBRgSsDCrZ3K40jg3Np9fOgQyakcHaqymK3bnxYB1F1oe8Ksts8TDDViROFgraoLzbWhfHVw==",
       "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-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==",
+    "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": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": ">=16 || 14 >=14.17"
       },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "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.24.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz",
-      "integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==",
+      "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"
       }
     },
     "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"
       }
     },
     "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": {
       }
     },
     "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": {
       }
     },
     "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": {
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
-    "node_modules/function-bind": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
-      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-      "dev": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/gauge": {
       "version": "2.7.4",
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
       }
     },
     "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.4",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.4.tgz",
+      "integrity": "sha512-ofbkKj+0pjXjhejr007J/fLf+sW+8H7K5GCm+msC8q3IpvgjobpyPqSRFemNyIMxklC0zeJpi7VDFna19FacvQ==",
       "dev": true,
       "dependencies": {
         "resolve-pkg-maps": "^1.0.0"
       }
     },
     "node_modules/glob": {
-      "version": "10.3.10",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-      "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+      "version": "10.3.12",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
+      "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
       "dev": true,
       "dependencies": {
         "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
+        "jackspeak": "^2.3.6",
         "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
+        "minipass": "^7.0.4",
+        "path-scurry": "^1.10.2"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
       }
     },
     "node_modules/glob/node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "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"
       }
     },
     "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.1.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.1.0.tgz",
+      "integrity": "sha512-926gJqg+4mkxwYKiFvoomM4J0kWESfk3qfTvRL2/oc/tK/eTDBbrfcKnSa2KtfdxB5onoL7D3A3qIHQFpd4+UA==",
       "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"
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
       "dev": true
     },
-    "node_modules/graphemer": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
-      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
-      "dev": true
-    },
     "node_modules/has-flag": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
         "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",
-      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "dev": true,
-      "dependencies": {
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/he": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
       "dev": true
     },
     "node_modules/html-validate": {
-      "version": "8.17.1",
-      "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-8.17.1.tgz",
-      "integrity": "sha512-jBcyyC7/O+ag/gSNfPMtjJ4HrSvASsxLv9FRgpZmK1BGHTF8l7zBibmWFRRYS/s+QsdmBF6dG9JyM1/378/Izw==",
+      "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": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/html-validate"
+        }
+      ],
+      "workspaces": [
+        "docs",
+        "tests/vitest"
+      ],
       "dependencies": {
         "@babel/code-frame": "^7.10.0",
         "@html-validate/stylish": "^4.1.0",
       }
     },
     "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",
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/is-core-module": {
-      "version": "2.13.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
-      "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
-      "dev": true,
-      "dependencies": {
-        "hasown": "^2.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
       "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",
       }
     },
     "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.0",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz",
+      "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==",
       "dev": true,
       "engines": {
         "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/mocha": {
-      "version": "10.3.0",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz",
-      "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==",
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
+      "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==",
       "dev": true,
       "dependencies": {
         "ansi-colors": "4.1.1",
       }
     },
     "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",
       }
     },
     "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"
       }
     },
     "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": {
         "node": ">=8"
       }
     },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
-    },
     "node_modules/path-scurry": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
-      "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
+      "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
       "dev": true,
       "dependencies": {
-        "lru-cache": "^9.1.1 || ^10.0.0",
+        "lru-cache": "^10.2.0",
         "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
       },
       "engines": {
       }
     },
     "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/picocolors": {
       "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
       "devOptional": true
     },
-    "node_modules/resolve": {
-      "version": "1.22.8",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
-      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
-      "dev": true,
-      "dependencies": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/resolve-alpn": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
       }
     },
     "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==",
+      "version": "7.6.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+      "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
       "devOptional": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
       "bin": {
         "semver": "bin/semver.js"
       },
         "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==",
-      "devOptional": true,
-      "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==",
-      "devOptional": true
-    },
     "node_modules/serialize-javascript": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
       }
     },
     "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",
       "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/sprintf-js": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
         "node": ">=8"
       }
     },
-    "node_modules/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+    "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": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "node": ">=6"
       }
     },
     "node_modules/tar": {
       }
     },
     "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": {
       }
     },
     "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": [
         {
         }
       ],
       "dependencies": {
-        "escalade": "^3.1.1",
+        "escalade": "^3.1.2",
         "picocolors": "^1.0.0"
       },
       "bin": {
         "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",
         "url": "https://github.com/sponsors/sindresorhus"
       }
     }
-  },
-  "dependencies": {
-    "@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
-    },
-    "@ampproject/remapping": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
-      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
-      "dev": true,
-      "requires": {
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      }
-    },
-    "@babel/code-frame": {
-      "version": "7.24.2",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
-      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
-      "dev": true,
-      "requires": {
-        "@babel/highlight": "^7.24.2",
-        "picocolors": "^1.0.0"
-      }
-    },
-    "@babel/compat-data": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz",
-      "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==",
-      "dev": true
-    },
-    "@babel/core": {
-      "version": "7.24.3",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz",
-      "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==",
-      "dev": true,
-      "requires": {
-        "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.24.2",
-        "@babel/generator": "^7.24.1",
-        "@babel/helper-compilation-targets": "^7.23.6",
-        "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.24.1",
-        "@babel/parser": "^7.24.1",
-        "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.1",
-        "@babel/types": "^7.24.0",
-        "convert-source-map": "^2.0.0",
-        "debug": "^4.1.0",
-        "gensync": "^1.0.0-beta.2",
-        "json5": "^2.2.3",
-        "semver": "^6.3.1"
-      },
-      "dependencies": {
-        "convert-source-map": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
-          "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
-          "dev": true
-        },
-        "semver": {
-          "version": "6.3.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-          "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-          "dev": true
-        }
-      }
-    },
-    "@babel/generator": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
-      "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.24.0",
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.25",
-        "jsesc": "^2.5.1"
-      }
-    },
-    "@babel/helper-compilation-targets": {
-      "version": "7.23.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
-      "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
-      "dev": true,
-      "requires": {
-        "@babel/compat-data": "^7.23.5",
-        "@babel/helper-validator-option": "^7.23.5",
-        "browserslist": "^4.22.2",
-        "lru-cache": "^5.1.1",
-        "semver": "^6.3.1"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "6.3.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-          "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-          "dev": true
-        }
-      }
-    },
-    "@babel/helper-environment-visitor": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
-      "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
-      "dev": true
-    },
-    "@babel/helper-function-name": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
-      "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
-      "dev": true,
-      "requires": {
-        "@babel/template": "^7.22.15",
-        "@babel/types": "^7.23.0"
-      }
-    },
-    "@babel/helper-hoist-variables": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
-      "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.22.5"
-      }
-    },
-    "@babel/helper-module-imports": {
-      "version": "7.24.3",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
-      "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.24.0"
-      }
-    },
-    "@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==",
-      "dev": true,
-      "requires": {
-        "@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-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==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.22.5"
-      }
-    },
-    "@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==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.22.5"
-      }
-    },
-    "@babel/helper-string-parser": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
-      "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
-      "dev": true
-    },
-    "@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==",
-      "dev": true
-    },
-    "@babel/helper-validator-option": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
-      "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
-      "dev": true
-    },
-    "@babel/helpers": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz",
-      "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==",
-      "dev": true,
-      "requires": {
-        "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.1",
-        "@babel/types": "^7.24.0"
-      }
-    },
-    "@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==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-validator-identifier": "^7.22.20",
-        "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0",
-        "picocolors": "^1.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-          "dev": true
-        },
-        "escape-string-regexp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
-      }
-    },
-    "@babel/parser": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
-      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
-      "dev": true
-    },
-    "@babel/template": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
-      "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.23.5",
-        "@babel/parser": "^7.24.0",
-        "@babel/types": "^7.24.0"
-      }
-    },
-    "@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==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.24.1",
-        "@babel/generator": "^7.24.1",
-        "@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",
-        "debug": "^4.3.1",
-        "globals": "^11.1.0"
-      },
-      "dependencies": {
-        "globals": {
-          "version": "11.12.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-          "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
-          "dev": true
-        }
-      }
-    },
-    "@babel/types": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
-      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-string-parser": "^7.23.4",
-        "@babel/helper-validator-identifier": "^7.22.20",
-        "to-fast-properties": "^2.0.0"
-      }
-    },
-    "@eslint-community/eslint-utils": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
-      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
-      "dev": true,
-      "requires": {
-        "eslint-visitor-keys": "^3.3.0"
-      }
-    },
-    "@eslint-community/regexpp": {
-      "version": "4.10.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
-      "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
-      "dev": true
-    },
-    "@eslint/eslintrc": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
-      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
-      "dev": true,
-      "requires": {
-        "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"
-      }
-    },
-    "@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
-    },
-    "@html-validate/stylish": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-4.2.0.tgz",
-      "integrity": "sha512-Nl8HCv0hGRSLQ+n1OD4Hk3a+Urwk9HH0vQkAzzCarT4KlA7bRl+6xEiS5PZVwOmjtC7XiH/oNe3as9Fxcr2A1w==",
-      "dev": true,
-      "requires": {
-        "kleur": "^4.0.0"
-      }
-    },
-    "@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,
-      "requires": {
-        "@humanwhocodes/object-schema": "^2.0.2",
-        "debug": "^4.3.1",
-        "minimatch": "^3.0.5"
-      }
-    },
-    "@humanwhocodes/module-importer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
-      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
-      "dev": true
-    },
-    "@humanwhocodes/object-schema": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
-      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
-      "dev": true
-    },
-    "@isaacs/cliui": {
-      "version": "8.0.2",
-      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
-      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
-      "dev": true,
-      "requires": {
-        "string-width": "^5.1.2",
-        "string-width-cjs": "npm:string-width@^4.2.0",
-        "strip-ansi": "^7.0.1",
-        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
-        "wrap-ansi": "^8.1.0",
-        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-          "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-          "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^6.0.1"
-          }
-        }
-      }
-    },
-    "@istanbuljs/load-nyc-config": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
-      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^5.3.1",
-        "find-up": "^4.1.0",
-        "get-package-type": "^0.1.0",
-        "js-yaml": "^3.13.1",
-        "resolve-from": "^5.0.0"
-      },
-      "dependencies": {
-        "argparse": {
-          "version": "1.0.10",
-          "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-          "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-          "dev": true,
-          "requires": {
-            "sprintf-js": "~1.0.2"
-          }
-        },
-        "find-up": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-          "dev": true,
-          "requires": {
-            "locate-path": "^5.0.0",
-            "path-exists": "^4.0.0"
-          }
-        },
-        "js-yaml": {
-          "version": "3.14.1",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-          "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
-          "dev": true,
-          "requires": {
-            "argparse": "^1.0.7",
-            "esprima": "^4.0.0"
-          }
-        },
-        "locate-path": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-          "dev": true,
-          "requires": {
-            "p-locate": "^4.1.0"
-          }
-        },
-        "p-limit": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-          "dev": true,
-          "requires": {
-            "p-try": "^2.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-          "dev": true,
-          "requires": {
-            "p-limit": "^2.2.0"
-          }
-        },
-        "resolve-from": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
-          "dev": true
-        },
-        "sprintf-js": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-          "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
-          "dev": true
-        }
-      }
-    },
-    "@istanbuljs/schema": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
-      "dev": true
-    },
-    "@jridgewell/gen-mapping": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
-      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
-      "dev": true,
-      "requires": {
-        "@jridgewell/set-array": "^1.2.1",
-        "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      }
-    },
-    "@jridgewell/resolve-uri": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
-      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-      "dev": true
-    },
-    "@jridgewell/set-array": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
-      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-      "dev": true
-    },
-    "@jridgewell/sourcemap-codec": {
-      "version": "1.4.15",
-      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
-      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
-      "dev": true
-    },
-    "@jridgewell/trace-mapping": {
-      "version": "0.3.25",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
-      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
-      "dev": true,
-      "requires": {
-        "@jridgewell/resolve-uri": "^3.1.0",
-        "@jridgewell/sourcemap-codec": "^1.4.14"
-      }
-    },
-    "@mapbox/node-pre-gyp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
-      "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
-      "optional": true,
-      "requires": {
-        "detect-libc": "^1.0.3",
-        "https-proxy-agent": "^5.0.0",
-        "make-dir": "^3.1.0",
-        "node-fetch": "^2.6.1",
-        "nopt": "^5.0.0",
-        "npmlog": "^4.1.2",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.4",
-        "tar": "^6.1.0"
-      }
-    },
-    "@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
-      "requires": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
-      }
-    },
-    "@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true
-    },
-    "@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
-      "requires": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
-      }
-    },
-    "@phc/format": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
-      "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==",
-      "optional": true
-    },
-    "@pkgjs/parseargs": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
-      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
-      "dev": true,
-      "optional": true
-    },
-    "@sidvind/better-ajv-errors": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-2.1.3.tgz",
-      "integrity": "sha512-lWuod/rh7Xz5uXiEGSfm2Sd5PG7K/6yJfoAZVqzsEswjPJhUz15R7Gn/o8RczA041QS15hBd/BCSeu9vwPArkA==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.16.0",
-        "chalk": "^4.1.0"
-      }
-    },
-    "@sindresorhus/is": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
-      "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="
-    },
-    "@sinonjs/commons": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
-      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
-      "dev": true,
-      "requires": {
-        "type-detect": "4.0.8"
-      }
-    },
-    "@sinonjs/fake-timers": {
-      "version": "11.2.2",
-      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz",
-      "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==",
-      "dev": true,
-      "requires": {
-        "@sinonjs/commons": "^3.0.0"
-      }
-    },
-    "@sinonjs/samsam": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
-      "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
-      "dev": true,
-      "requires": {
-        "@sinonjs/commons": "^2.0.0",
-        "lodash.get": "^4.4.2",
-        "type-detect": "^4.0.8"
-      },
-      "dependencies": {
-        "@sinonjs/commons": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
-          "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
-          "dev": true,
-          "requires": {
-            "type-detect": "4.0.8"
-          }
-        }
-      }
-    },
-    "@sinonjs/text-encoding": {
-      "version": "0.7.2",
-      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
-      "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==",
-      "dev": true
-    },
-    "@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==",
-      "requires": {
-        "@squeep/log-helper": "^1.0.0",
-        "mime-db": "^1.52.0",
-        "uuid": "^9.0.1"
-      }
-    },
-    "@squeep/html-template-helper": {
-      "version": "git+https://git.squeep.com/squeep-html-template-helper#2d0ba72a2ea35f45c1ab1ac81fce3d0cbe7db419",
-      "from": "@squeep/html-template-helper@git+https://git.squeep.com/squeep-html-template-helper#v1.6.0",
-      "requires": {
-        "@squeep/lazy-property": "^1.1.2"
-      }
-    },
-    "@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==",
-      "requires": {
-        "@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/lazy-property": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@squeep/lazy-property/-/lazy-property-1.1.2.tgz",
-      "integrity": "sha512-wRdR4IOqWXoDMArx0HPo5MtM2Wk5wemAULbZ6PabVw1ylSQekkzKfoAUuupxsKuzjcRPjZvbpGDv+i04hBMnQw=="
-    },
-    "@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=="
-    },
-    "@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=="
-    },
-    "@squeep/totp": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/@squeep/totp/-/totp-1.1.4.tgz",
-      "integrity": "sha512-cMoicNB5xIDMdcOtTfkzWZ0eQCepatTsFoWXtQ8Ja4FfvAA3ZWwIMfKV4K7zbx1MjYGF/Ufikxa6CaPS6yd5mw==",
-      "requires": {
-        "base32.js": "^0.1.0",
-        "qrcode-svg": "^1.1.0"
-      }
-    },
-    "@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=="
-    },
-    "@szmarczak/http-timer": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
-      "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
-      "requires": {
-        "defer-to-connect": "^2.0.1"
-      }
-    },
-    "@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=="
-    },
-    "@ungap/structured-clone": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
-      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
-      "dev": true
-    },
-    "abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
-      "optional": true
-    },
-    "acorn": {
-      "version": "8.11.3",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
-      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
-      "dev": true
-    },
-    "acorn-jsx": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
-      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
-      "dev": true,
-      "requires": {}
-    },
-    "agent-base": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
-      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
-      "optional": true,
-      "requires": {
-        "debug": "4"
-      }
-    },
-    "aggregate-error": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
-      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
-      "dev": true,
-      "requires": {
-        "clean-stack": "^2.0.0",
-        "indent-string": "^4.0.0"
-      }
-    },
-    "ajv": {
-      "version": "6.12.6",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-      "dev": true,
-      "requires": {
-        "fast-deep-equal": "^3.1.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      }
-    },
-    "ansi-colors": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
-      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
-      "dev": true
-    },
-    "ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "devOptional": true
-    },
-    "ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "devOptional": true,
-      "requires": {
-        "color-convert": "^2.0.1"
-      }
-    },
-    "anymatch": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
-      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
-      "dev": true,
-      "requires": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      }
-    },
-    "append-transform": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
-      "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
-      "dev": true,
-      "requires": {
-        "default-require-extensions": "^3.0.0"
-      }
-    },
-    "aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "optional": true
-    },
-    "archy": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
-      "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
-      "dev": true
-    },
-    "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",
-      "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-      "optional": true,
-      "requires": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
-    "argon2": {
-      "version": "0.40.1",
-      "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz",
-      "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==",
-      "optional": true,
-      "requires": {
-        "@phc/format": "^1.0.0",
-        "node-addon-api": "^7.1.0",
-        "node-gyp-build": "^4.8.0"
-      }
-    },
-    "argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true
-    },
-    "balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "devOptional": true
-    },
-    "base32.js": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz",
-      "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ=="
-    },
-    "binary-extensions": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
-      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
-      "dev": true
-    },
-    "bindings": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-      "optional": true,
-      "requires": {
-        "file-uri-to-path": "1.0.0"
-      }
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "devOptional": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "requires": {
-        "fill-range": "^7.0.1"
-      }
-    },
-    "browser-stdout": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
-      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
-      "dev": true
-    },
-    "browserslist": {
-      "version": "4.23.0",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
-      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
-      "dev": true,
-      "requires": {
-        "caniuse-lite": "^1.0.30001587",
-        "electron-to-chromium": "^1.4.668",
-        "node-releases": "^2.0.14",
-        "update-browserslist-db": "^1.0.13"
-      }
-    },
-    "buffer-from": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-      "dev": true
-    },
-    "builtin-modules": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
-      "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
-      "dev": true
-    },
-    "builtins": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
-      "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
-      "dev": true,
-      "requires": {
-        "semver": "^7.0.0"
-      }
-    },
-    "cacheable-lookup": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
-      "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="
-    },
-    "cacheable-request": {
-      "version": "10.2.14",
-      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
-      "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
-      "requires": {
-        "@types/http-cache-semantics": "^4.0.2",
-        "get-stream": "^6.0.1",
-        "http-cache-semantics": "^4.1.1",
-        "keyv": "^4.5.3",
-        "mimic-response": "^4.0.0",
-        "normalize-url": "^8.0.0",
-        "responselike": "^3.0.0"
-      }
-    },
-    "caching-transform": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
-      "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
-      "dev": true,
-      "requires": {
-        "hasha": "^5.0.0",
-        "make-dir": "^3.0.0",
-        "package-hash": "^4.0.0",
-        "write-file-atomic": "^3.0.0"
-      }
-    },
-    "callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true
-    },
-    "camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "devOptional": true
-    },
-    "caniuse-lite": {
-      "version": "1.0.30001600",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
-      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
-      "dev": true
-    },
-    "chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      }
-    },
-    "chokidar": {
-      "version": "3.5.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
-      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
-      "dev": true,
-      "requires": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "fsevents": "~2.3.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      },
-      "dependencies": {
-        "glob-parent": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-          "dev": true,
-          "requires": {
-            "is-glob": "^4.0.1"
-          }
-        }
-      }
-    },
-    "chownr": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
-      "optional": true
-    },
-    "clean-stack": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
-      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
-      "dev": true
-    },
-    "cliui": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
-      "dev": true,
-      "requires": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.0",
-        "wrap-ansi": "^7.0.0"
-      },
-      "dependencies": {
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "wrap-ansi": {
-          "version": "7.0.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-          "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^4.0.0",
-            "string-width": "^4.1.0",
-            "strip-ansi": "^6.0.0"
-          }
-        }
-      }
-    },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
-      "optional": true
-    },
-    "color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "devOptional": true,
-      "requires": {
-        "color-name": "~1.1.4"
-      }
-    },
-    "color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "devOptional": true
-    },
-    "commondir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
-      "devOptional": true
-    },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
-      }
-    },
-    "console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
-      "optional": true
-    },
-    "convert-source-map": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
-      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
-      "dev": true
-    },
-    "core-util-is": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
-      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
-      "devOptional": true
-    },
-    "cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
-      "dev": true,
-      "requires": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      }
-    },
-    "debug": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
-      "devOptional": true,
-      "requires": {
-        "ms": "2.1.2"
-      }
-    },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
-      "devOptional": true
-    },
-    "decompress-response": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
-      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
-      "requires": {
-        "mimic-response": "^3.1.0"
-      },
-      "dependencies": {
-        "mimic-response": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
-          "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
-        }
-      }
-    },
-    "deep-is": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
-      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
-      "dev": true
-    },
-    "deepmerge": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
-      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
-      "dev": true
-    },
-    "default-require-extensions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz",
-      "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==",
-      "dev": true,
-      "requires": {
-        "strip-bom": "^4.0.0"
-      }
-    },
-    "defer-to-connect": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
-      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
-    },
-    "delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
-      "optional": true
-    },
-    "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
-    },
-    "diff": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
-      "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
-      "dev": true
-    },
-    "doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
-      "dev": true,
-      "requires": {
-        "esutils": "^2.0.2"
-      }
-    },
-    "eastasianwidth": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
-      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
-      "dev": true
-    },
-    "electron-to-chromium": {
-      "version": "1.4.715",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
-      "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==",
-      "dev": true
-    },
-    "emoji-regex": {
-      "version": "9.2.2",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
-      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
-      "dev": true
-    },
-    "entities": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
-      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
-    },
-    "es6-error": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
-      "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
-      "dev": true
-    },
-    "escalade": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
-      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
-      "dev": true
-    },
-    "escape-string-regexp": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
-      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-      "dev": true
-    },
-    "eslint": {
-      "version": "8.57.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
-      "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
-      "dev": true,
-      "requires": {
-        "@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"
-      }
-    },
-    "eslint-compat-utils": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz",
-      "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==",
-      "dev": true,
-      "requires": {
-        "semver": "^7.5.4"
-      }
-    },
-    "eslint-plugin-es-x": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz",
-      "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==",
-      "dev": true,
-      "requires": {
-        "@eslint-community/eslint-utils": "^4.1.2",
-        "@eslint-community/regexpp": "^4.6.0",
-        "eslint-compat-utils": "^0.5.0"
-      }
-    },
-    "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==",
-      "dev": true,
-      "requires": {
-        "@eslint-community/eslint-utils": "^4.4.0",
-        "builtins": "^5.0.1",
-        "eslint-plugin-es-x": "^7.5.0",
-        "get-tsconfig": "^4.7.0",
-        "globals": "^13.24.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",
-        "semver": "^7.5.3"
-      }
-    },
-    "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,
-      "requires": {}
-    },
-    "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==",
-      "dev": true,
-      "requires": {
-        "safe-regex": "^2.1.1"
-      }
-    },
-    "eslint-plugin-sonarjs": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz",
-      "integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==",
-      "dev": true,
-      "requires": {}
-    },
-    "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,
-      "requires": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^5.2.0"
-      }
-    },
-    "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
-    },
-    "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,
-      "requires": {
-        "acorn": "^8.9.0",
-        "acorn-jsx": "^5.3.2",
-        "eslint-visitor-keys": "^3.4.1"
-      }
-    },
-    "esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true
-    },
-    "esquery": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
-      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^5.1.0"
-      }
-    },
-    "esrecurse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
-      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^5.2.0"
-      }
-    },
-    "estraverse": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
-      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-      "dev": true
-    },
-    "esutils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
-      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
-      "dev": true
-    },
-    "fast-deep-equal": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
-      "dev": true
-    },
-    "fast-json-stable-stringify": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
-      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
-      "dev": true
-    },
-    "fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
-      "dev": true
-    },
-    "fastq": {
-      "version": "1.17.1",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
-      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
-      "dev": true,
-      "requires": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "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,
-      "requires": {
-        "flat-cache": "^3.0.4"
-      }
-    },
-    "file-uri-to-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-      "optional": true
-    },
-    "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "requires": {
-        "to-regex-range": "^5.0.1"
-      }
-    },
-    "find-cache-dir": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
-      "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
-      "dev": true,
-      "requires": {
-        "commondir": "^1.0.1",
-        "make-dir": "^3.0.2",
-        "pkg-dir": "^4.1.0"
-      }
-    },
-    "find-up": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
-      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
-      "dev": true,
-      "requires": {
-        "locate-path": "^6.0.0",
-        "path-exists": "^4.0.0"
-      }
-    },
-    "flat": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
-      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
-      "dev": true
-    },
-    "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,
-      "requires": {
-        "flatted": "^3.2.9",
-        "keyv": "^4.5.3",
-        "rimraf": "^3.0.2"
-      }
-    },
-    "flatted": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
-      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
-      "dev": true
-    },
-    "foreground-child": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
-      "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^7.0.0",
-        "signal-exit": "^4.0.1"
-      }
-    },
-    "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=="
-    },
-    "fromentries": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
-      "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
-      "dev": true
-    },
-    "fs-minipass": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
-      "optional": true,
-      "requires": {
-        "minipass": "^3.0.0"
-      },
-      "dependencies": {
-        "minipass": {
-          "version": "3.3.6",
-          "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-          "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-          "optional": true,
-          "requires": {
-            "yallist": "^4.0.0"
-          }
-        },
-        "yallist": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-          "optional": true
-        }
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
-      "devOptional": true
-    },
-    "fsevents": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
-      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "dev": true,
-      "optional": true
-    },
-    "function-bind": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
-      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-      "dev": true
-    },
-    "gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-      "optional": true,
-      "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-          "optional": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-          "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
-          "optional": true,
-          "requires": {
-            "number-is-nan": "^1.0.0"
-          }
-        },
-        "signal-exit": {
-          "version": "3.0.7",
-          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-          "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-          "optional": true
-        },
-        "string-width": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-          "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
-          "optional": true,
-          "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-          "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
-          "optional": true,
-          "requires": {
-            "ansi-regex": "^2.0.0"
-          }
-        }
-      }
-    },
-    "gensync": {
-      "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
-      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
-      "dev": true
-    },
-    "get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "devOptional": true
-    },
-    "get-package-type": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
-      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
-      "dev": true
-    },
-    "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=="
-    },
-    "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==",
-      "dev": true,
-      "requires": {
-        "resolve-pkg-maps": "^1.0.0"
-      }
-    },
-    "glob": {
-      "version": "10.3.10",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-      "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
-      "dev": true,
-      "requires": {
-        "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
-        "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
-      },
-      "dependencies": {
-        "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,
-          "requires": {
-            "balanced-match": "^1.0.0"
-          }
-        },
-        "minimatch": {
-          "version": "9.0.3",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-          "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
-          "dev": true,
-          "requires": {
-            "brace-expansion": "^2.0.1"
-          }
-        }
-      }
-    },
-    "glob-parent": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
-      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
-      "dev": true,
-      "requires": {
-        "is-glob": "^4.0.3"
-      }
-    },
-    "globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
-      "dev": true,
-      "requires": {
-        "type-fest": "^0.20.2"
-      }
-    },
-    "got": {
-      "version": "13.0.0",
-      "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
-      "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
-      "requires": {
-        "@sindresorhus/is": "^5.2.0",
-        "@szmarczak/http-timer": "^5.0.1",
-        "cacheable-lookup": "^7.0.0",
-        "cacheable-request": "^10.2.8",
-        "decompress-response": "^6.0.0",
-        "form-data-encoder": "^2.1.2",
-        "get-stream": "^6.0.1",
-        "http2-wrapper": "^2.1.10",
-        "lowercase-keys": "^3.0.0",
-        "p-cancelable": "^3.0.0",
-        "responselike": "^3.0.0"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.2.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
-      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
-      "dev": true
-    },
-    "graphemer": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
-      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
-      "dev": true
-    },
-    "has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true
-    },
-    "has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
-      "optional": true
-    },
-    "hasha": {
-      "version": "5.2.2",
-      "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
-      "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
-      "dev": true,
-      "requires": {
-        "is-stream": "^2.0.0",
-        "type-fest": "^0.8.0"
-      },
-      "dependencies": {
-        "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
-        }
-      }
-    },
-    "hasown": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
-      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.2"
-      }
-    },
-    "he": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
-      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
-      "dev": true
-    },
-    "html-escaper": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
-      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
-      "dev": true
-    },
-    "html-validate": {
-      "version": "8.17.1",
-      "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-8.17.1.tgz",
-      "integrity": "sha512-jBcyyC7/O+ag/gSNfPMtjJ4HrSvASsxLv9FRgpZmK1BGHTF8l7zBibmWFRRYS/s+QsdmBF6dG9JyM1/378/Izw==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.10.0",
-        "@html-validate/stylish": "^4.1.0",
-        "@sidvind/better-ajv-errors": "2.1.3",
-        "ajv": "^8.0.0",
-        "deepmerge": "4.3.1",
-        "glob": "^10.0.0",
-        "ignore": "5.3.1",
-        "kleur": "^4.1.0",
-        "minimist": "^1.2.0",
-        "prompts": "^2.0.0",
-        "semver": "^7.0.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "8.12.0",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
-          "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
-          "dev": true,
-          "requires": {
-            "fast-deep-equal": "^3.1.1",
-            "json-schema-traverse": "^1.0.0",
-            "require-from-string": "^2.0.2",
-            "uri-js": "^4.2.2"
-          }
-        },
-        "json-schema-traverse": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-          "dev": true
-        }
-      }
-    },
-    "http-cache-semantics": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
-      "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
-    },
-    "http2-wrapper": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
-      "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
-      "requires": {
-        "quick-lru": "^5.1.1",
-        "resolve-alpn": "^1.2.0"
-      }
-    },
-    "https-proxy-agent": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
-      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
-      "optional": true,
-      "requires": {
-        "agent-base": "6",
-        "debug": "4"
-      }
-    },
-    "iconv": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/iconv/-/iconv-3.0.1.tgz",
-      "integrity": "sha512-lJnFLxVc0d82R7GfU7a9RujKVUQ3Eee19tPKWZWBJtAEGRHVEyFzCtbNl3GPKuDnHBBRT4/nDS4Ru9AIDT72qA=="
-    },
-    "ignore": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
-      "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
-      "dev": true
-    },
-    "import-fresh": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
-      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
-      "dev": true,
-      "requires": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      }
-    },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
-      "dev": true
-    },
-    "indent-string": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
-      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "devOptional": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "devOptional": true
-    },
-    "ip-address": {
-      "version": "9.0.5",
-      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
-      "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
-      "requires": {
-        "jsbn": "1.1.0",
-        "sprintf-js": "^1.1.3"
-      }
-    },
-    "is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "requires": {
-        "binary-extensions": "^2.0.0"
-      }
-    },
-    "is-builtin-module": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
-      "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
-      "dev": true,
-      "requires": {
-        "builtin-modules": "^3.3.0"
-      }
-    },
-    "is-core-module": {
-      "version": "2.13.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
-      "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
-      "dev": true,
-      "requires": {
-        "hasown": "^2.0.0"
-      }
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true
-    },
-    "is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "devOptional": true
-    },
-    "is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
-    },
-    "is-path-inside": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
-      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
-      "dev": true
-    },
-    "is-plain-obj": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
-      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
-      "dev": true
-    },
-    "is-stream": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-      "dev": true
-    },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
-      "dev": true
-    },
-    "is-unicode-supported": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
-      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
-      "dev": true
-    },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
-      "dev": true
-    },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
-      "devOptional": true
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true
-    },
-    "istanbul-lib-coverage": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
-      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
-      "dev": true
-    },
-    "istanbul-lib-hook": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
-      "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
-      "dev": true,
-      "requires": {
-        "append-transform": "^2.0.0"
-      }
-    },
-    "istanbul-lib-instrument": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
-      "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
-      "dev": true,
-      "requires": {
-        "@babel/core": "^7.7.5",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-coverage": "^3.0.0",
-        "semver": "^6.3.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "6.3.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-          "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-          "dev": true
-        }
-      }
-    },
-    "istanbul-lib-processinfo": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz",
-      "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==",
-      "dev": true,
-      "requires": {
-        "archy": "^1.0.0",
-        "cross-spawn": "^7.0.3",
-        "istanbul-lib-coverage": "^3.2.0",
-        "p-map": "^3.0.0",
-        "rimraf": "^3.0.0",
-        "uuid": "^8.3.2"
-      },
-      "dependencies": {
-        "uuid": {
-          "version": "8.3.2",
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-          "dev": true
-        }
-      }
-    },
-    "istanbul-lib-report": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
-      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
-      "dev": true,
-      "requires": {
-        "istanbul-lib-coverage": "^3.0.0",
-        "make-dir": "^4.0.0",
-        "supports-color": "^7.1.0"
-      },
-      "dependencies": {
-        "make-dir": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
-          "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
-          "dev": true,
-          "requires": {
-            "semver": "^7.5.3"
-          }
-        }
-      }
-    },
-    "istanbul-lib-source-maps": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
-      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
-      "dev": true,
-      "requires": {
-        "debug": "^4.1.1",
-        "istanbul-lib-coverage": "^3.0.0",
-        "source-map": "^0.6.1"
-      }
-    },
-    "istanbul-reports": {
-      "version": "3.1.7",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
-      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
-      "dev": true,
-      "requires": {
-        "html-escaper": "^2.0.0",
-        "istanbul-lib-report": "^3.0.0"
-      }
-    },
-    "jackspeak": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
-      "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
-      "dev": true,
-      "requires": {
-        "@isaacs/cliui": "^8.0.2",
-        "@pkgjs/parseargs": "^0.11.0"
-      }
-    },
-    "js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "dev": true
-    },
-    "js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "dev": true,
-      "requires": {
-        "argparse": "^2.0.1"
-      }
-    },
-    "jsbn": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
-      "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
-    },
-    "jsesc": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
-      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
-      "dev": true
-    },
-    "json-buffer": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
-    },
-    "json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true
-    },
-    "json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
-      "dev": true
-    },
-    "json5": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
-      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
-      "dev": true
-    },
-    "just-extend": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
-      "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
-      "dev": true
-    },
-    "keyv": {
-      "version": "4.5.4",
-      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
-      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
-      "requires": {
-        "json-buffer": "3.0.1"
-      }
-    },
-    "kleur": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
-      "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
-      "dev": true
-    },
-    "levn": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
-      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
-      "dev": true,
-      "requires": {
-        "prelude-ls": "^1.2.1",
-        "type-check": "~0.4.0"
-      }
-    },
-    "locate-path": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
-      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
-      "dev": true,
-      "requires": {
-        "p-locate": "^5.0.0"
-      }
-    },
-    "lodash.flattendeep": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
-      "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
-      "dev": true
-    },
-    "lodash.get": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
-      "dev": true
-    },
-    "lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true
-    },
-    "log-symbols": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
-      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
-      "dev": true,
-      "requires": {
-        "chalk": "^4.1.0",
-        "is-unicode-supported": "^0.1.0"
-      }
-    },
-    "lowercase-keys": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
-      "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
-    },
-    "lru-cache": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-      "dev": true,
-      "requires": {
-        "yallist": "^3.0.2"
-      }
-    },
-    "make-dir": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
-      "devOptional": true,
-      "requires": {
-        "semver": "^6.0.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "6.3.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-          "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-          "devOptional": true
-        }
-      }
-    },
-    "microformats-parser": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/microformats-parser/-/microformats-parser-2.0.2.tgz",
-      "integrity": "sha512-tUf9DmN4Jq/tGyp1YH2V6D/Cud+9Uc0WhjjUFirqVeHTRkkfLDacv6BQFT7h7HFsD0Z8wja5eKkRgzZU8bv0Fw==",
-      "requires": {
-        "parse5": "^7.1.2"
-      }
-    },
-    "mime-db": {
-      "version": "1.52.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
-      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
-    },
-    "mimic-response": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
-      "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="
-    },
-    "minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "devOptional": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "minimist": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true
-    },
-    "minipass": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
-      "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
-      "dev": true
-    },
-    "minizlib": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
-      "optional": true,
-      "requires": {
-        "minipass": "^3.0.0",
-        "yallist": "^4.0.0"
-      },
-      "dependencies": {
-        "minipass": {
-          "version": "3.3.6",
-          "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-          "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-          "optional": true,
-          "requires": {
-            "yallist": "^4.0.0"
-          }
-        },
-        "yallist": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-          "optional": true
-        }
-      }
-    },
-    "mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "optional": true
-    },
-    "mocha": {
-      "version": "10.3.0",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz",
-      "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==",
-      "dev": true,
-      "requires": {
-        "ansi-colors": "4.1.1",
-        "browser-stdout": "1.3.1",
-        "chokidar": "3.5.3",
-        "debug": "4.3.4",
-        "diff": "5.0.0",
-        "escape-string-regexp": "4.0.0",
-        "find-up": "5.0.0",
-        "glob": "8.1.0",
-        "he": "1.2.0",
-        "js-yaml": "4.1.0",
-        "log-symbols": "4.1.0",
-        "minimatch": "5.0.1",
-        "ms": "2.1.3",
-        "serialize-javascript": "6.0.0",
-        "strip-json-comments": "3.1.1",
-        "supports-color": "8.1.1",
-        "workerpool": "6.2.1",
-        "yargs": "16.2.0",
-        "yargs-parser": "20.2.4",
-        "yargs-unparser": "2.0.0"
-      },
-      "dependencies": {
-        "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,
-          "requires": {
-            "balanced-match": "^1.0.0"
-          }
-        },
-        "glob": {
-          "version": "8.1.0",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-          "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^5.0.1",
-            "once": "^1.3.0"
-          }
-        },
-        "minimatch": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
-          "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
-          "dev": true,
-          "requires": {
-            "brace-expansion": "^2.0.1"
-          }
-        },
-        "ms": {
-          "version": "2.1.3",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "8.1.1",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
-          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
-      }
-    },
-    "ms": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "devOptional": true
-    },
-    "natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
-      "dev": true
-    },
-    "nise": {
-      "version": "5.1.9",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz",
-      "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==",
-      "dev": true,
-      "requires": {
-        "@sinonjs/commons": "^3.0.0",
-        "@sinonjs/fake-timers": "^11.2.2",
-        "@sinonjs/text-encoding": "^0.7.2",
-        "just-extend": "^6.2.0",
-        "path-to-regexp": "^6.2.1"
-      }
-    },
-    "node-addon-api": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz",
-      "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==",
-      "optional": true
-    },
-    "node-fetch": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
-      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
-      "optional": true,
-      "requires": {
-        "whatwg-url": "^5.0.0"
-      }
-    },
-    "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==",
-      "optional": true
-    },
-    "node-linux-pam": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/node-linux-pam/-/node-linux-pam-0.2.1.tgz",
-      "integrity": "sha512-OeMZW0Bs1bffsvXI/bJQbU0rkiWTOo0ceT6+mrbU84TJ33vAKykIZrLI+ApfRqkBQW5jzW5rJ7x+NSyToafqig==",
-      "optional": true,
-      "requires": {
-        "@mapbox/node-pre-gyp": "1.0.5",
-        "bindings": "1.5.0",
-        "node-addon-api": "3.1.0",
-        "string-template": "1.0.0",
-        "yargs": "15.4.1"
-      },
-      "dependencies": {
-        "cliui": {
-          "version": "6.0.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
-          "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
-          "optional": true,
-          "requires": {
-            "string-width": "^4.2.0",
-            "strip-ansi": "^6.0.0",
-            "wrap-ansi": "^6.2.0"
-          }
-        },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "optional": true
-        },
-        "find-up": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-          "optional": true,
-          "requires": {
-            "locate-path": "^5.0.0",
-            "path-exists": "^4.0.0"
-          }
-        },
-        "locate-path": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-          "optional": true,
-          "requires": {
-            "p-locate": "^4.1.0"
-          }
-        },
-        "node-addon-api": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz",
-          "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==",
-          "optional": true
-        },
-        "p-limit": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-          "optional": true,
-          "requires": {
-            "p-try": "^2.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-          "optional": true,
-          "requires": {
-            "p-limit": "^2.2.0"
-          }
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "optional": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "wrap-ansi": {
-          "version": "6.2.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
-          "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
-          "optional": true,
-          "requires": {
-            "ansi-styles": "^4.0.0",
-            "string-width": "^4.1.0",
-            "strip-ansi": "^6.0.0"
-          }
-        },
-        "y18n": {
-          "version": "4.0.3",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-          "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
-          "optional": true
-        },
-        "yargs": {
-          "version": "15.4.1",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
-          "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
-          "optional": true,
-          "requires": {
-            "cliui": "^6.0.0",
-            "decamelize": "^1.2.0",
-            "find-up": "^4.1.0",
-            "get-caller-file": "^2.0.1",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^4.2.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^18.1.2"
-          }
-        },
-        "yargs-parser": {
-          "version": "18.1.3",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
-          "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
-          "optional": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
-        }
-      }
-    },
-    "node-preload": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
-      "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
-      "dev": true,
-      "requires": {
-        "process-on-spawn": "^1.0.0"
-      }
-    },
-    "node-releases": {
-      "version": "2.0.14",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
-      "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
-      "dev": true
-    },
-    "nopt": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
-      "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
-      "optional": true,
-      "requires": {
-        "abbrev": "1"
-      }
-    },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true
-    },
-    "normalize-url": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
-      "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w=="
-    },
-    "npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
-      "optional": true,
-      "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
-      }
-    },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
-      "optional": true
-    },
-    "nyc": {
-      "version": "15.1.0",
-      "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
-      "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==",
-      "dev": true,
-      "requires": {
-        "@istanbuljs/load-nyc-config": "^1.0.0",
-        "@istanbuljs/schema": "^0.1.2",
-        "caching-transform": "^4.0.0",
-        "convert-source-map": "^1.7.0",
-        "decamelize": "^1.2.0",
-        "find-cache-dir": "^3.2.0",
-        "find-up": "^4.1.0",
-        "foreground-child": "^2.0.0",
-        "get-package-type": "^0.1.0",
-        "glob": "^7.1.6",
-        "istanbul-lib-coverage": "^3.0.0",
-        "istanbul-lib-hook": "^3.0.0",
-        "istanbul-lib-instrument": "^4.0.0",
-        "istanbul-lib-processinfo": "^2.0.2",
-        "istanbul-lib-report": "^3.0.0",
-        "istanbul-lib-source-maps": "^4.0.0",
-        "istanbul-reports": "^3.0.2",
-        "make-dir": "^3.0.0",
-        "node-preload": "^0.2.1",
-        "p-map": "^3.0.0",
-        "process-on-spawn": "^1.0.0",
-        "resolve-from": "^5.0.0",
-        "rimraf": "^3.0.0",
-        "signal-exit": "^3.0.2",
-        "spawn-wrap": "^2.0.0",
-        "test-exclude": "^6.0.0",
-        "yargs": "^15.0.2"
-      },
-      "dependencies": {
-        "cliui": {
-          "version": "6.0.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
-          "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
-          "dev": true,
-          "requires": {
-            "string-width": "^4.2.0",
-            "strip-ansi": "^6.0.0",
-            "wrap-ansi": "^6.2.0"
-          }
-        },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "find-up": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-          "dev": true,
-          "requires": {
-            "locate-path": "^5.0.0",
-            "path-exists": "^4.0.0"
-          }
-        },
-        "foreground-child": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
-          "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
-          "dev": true,
-          "requires": {
-            "cross-spawn": "^7.0.0",
-            "signal-exit": "^3.0.2"
-          }
-        },
-        "glob": {
-          "version": "7.2.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.1.1",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "locate-path": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-          "dev": true,
-          "requires": {
-            "p-locate": "^4.1.0"
-          }
-        },
-        "p-limit": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-          "dev": true,
-          "requires": {
-            "p-try": "^2.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-          "dev": true,
-          "requires": {
-            "p-limit": "^2.2.0"
-          }
-        },
-        "resolve-from": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
-          "dev": true
-        },
-        "signal-exit": {
-          "version": "3.0.7",
-          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-          "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "wrap-ansi": {
-          "version": "6.2.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
-          "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^4.0.0",
-            "string-width": "^4.1.0",
-            "strip-ansi": "^6.0.0"
-          }
-        },
-        "y18n": {
-          "version": "4.0.3",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-          "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
-          "dev": true
-        },
-        "yargs": {
-          "version": "15.4.1",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
-          "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
-          "dev": true,
-          "requires": {
-            "cliui": "^6.0.0",
-            "decamelize": "^1.2.0",
-            "find-up": "^4.1.0",
-            "get-caller-file": "^2.0.1",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^4.2.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^18.1.2"
-          }
-        },
-        "yargs-parser": {
-          "version": "18.1.3",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
-          "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
-        }
-      }
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "optional": true
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "devOptional": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "optionator": {
-      "version": "0.9.3",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
-      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
-      "dev": true,
-      "requires": {
-        "@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"
-      }
-    },
-    "os-shim": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
-      "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==",
-      "dev": true
-    },
-    "p-cancelable": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
-      "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="
-    },
-    "p-limit": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
-      "dev": true,
-      "requires": {
-        "yocto-queue": "^0.1.0"
-      }
-    },
-    "p-locate": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
-      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
-      "dev": true,
-      "requires": {
-        "p-limit": "^3.0.2"
-      }
-    },
-    "p-map": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
-      "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
-      "dev": true,
-      "requires": {
-        "aggregate-error": "^3.0.0"
-      }
-    },
-    "p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "devOptional": true
-    },
-    "package-hash": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz",
-      "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.15",
-        "hasha": "^5.0.0",
-        "lodash.flattendeep": "^4.4.0",
-        "release-zalgo": "^1.0.0"
-      }
-    },
-    "parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "requires": {
-        "callsites": "^3.0.0"
-      }
-    },
-    "parse5": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
-      "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
-      "requires": {
-        "entities": "^4.4.0"
-      }
-    },
-    "path-exists": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-      "devOptional": true
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
-      "devOptional": true
-    },
-    "path-key": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
-      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
-    },
-    "path-scurry": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
-      "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
-      "dev": true,
-      "requires": {
-        "lru-cache": "^9.1.1 || ^10.0.0",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
-      },
-      "dependencies": {
-        "lru-cache": {
-          "version": "10.2.0",
-          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
-          "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
-          "dev": true
-        }
-      }
-    },
-    "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==",
-      "dev": true
-    },
-    "picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-      "dev": true
-    },
-    "picomatch": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true
-    },
-    "pkg-dir": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
-      "dev": true,
-      "requires": {
-        "find-up": "^4.0.0"
-      },
-      "dependencies": {
-        "find-up": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-          "dev": true,
-          "requires": {
-            "locate-path": "^5.0.0",
-            "path-exists": "^4.0.0"
-          }
-        },
-        "locate-path": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-          "dev": true,
-          "requires": {
-            "p-locate": "^4.1.0"
-          }
-        },
-        "p-limit": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-          "dev": true,
-          "requires": {
-            "p-try": "^2.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-          "dev": true,
-          "requires": {
-            "p-limit": "^2.2.0"
-          }
-        }
-      }
-    },
-    "pre-commit": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
-      "integrity": "sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA==",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^5.0.1",
-        "spawn-sync": "^1.0.15",
-        "which": "1.2.x"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
-          "dev": true,
-          "requires": {
-            "lru-cache": "^4.0.1",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "lru-cache": {
-          "version": "4.1.5",
-          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-          "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
-          "dev": true,
-          "requires": {
-            "pseudomap": "^1.0.2",
-            "yallist": "^2.1.2"
-          }
-        },
-        "shebang-command": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-          "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
-          "dev": true,
-          "requires": {
-            "shebang-regex": "^1.0.0"
-          }
-        },
-        "shebang-regex": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-          "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
-          "dev": true
-        },
-        "which": {
-          "version": "1.2.14",
-          "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
-          "integrity": "sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw==",
-          "dev": true,
-          "requires": {
-            "isexe": "^2.0.0"
-          }
-        },
-        "yallist": {
-          "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
-          "dev": true
-        }
-      }
-    },
-    "prelude-ls": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
-      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
-      "dev": true
-    },
-    "process-nextick-args": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
-      "devOptional": true
-    },
-    "process-on-spawn": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz",
-      "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==",
-      "dev": true,
-      "requires": {
-        "fromentries": "^1.2.0"
-      }
-    },
-    "prompts": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
-      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
-      "dev": true,
-      "requires": {
-        "kleur": "^3.0.3",
-        "sisteransi": "^1.0.5"
-      },
-      "dependencies": {
-        "kleur": {
-          "version": "3.0.3",
-          "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
-          "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
-          "dev": true
-        }
-      }
-    },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
-      "dev": true
-    },
-    "punycode": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
-      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-      "dev": true
-    },
-    "qrcode-svg": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz",
-      "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw=="
-    },
-    "queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true
-    },
-    "quick-lru": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
-    },
-    "randombytes": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
-      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "readable-stream": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
-      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-      "devOptional": true,
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      }
-    },
-    "readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "requires": {
-        "picomatch": "^2.2.1"
-      }
-    },
-    "regexp-tree": {
-      "version": "0.1.27",
-      "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
-      "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
-      "dev": true
-    },
-    "release-zalgo": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
-      "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==",
-      "dev": true,
-      "requires": {
-        "es6-error": "^4.0.1"
-      }
-    },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "devOptional": true
-    },
-    "require-from-string": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
-      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
-      "dev": true
-    },
-    "require-main-filename": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-      "devOptional": true
-    },
-    "resolve": {
-      "version": "1.22.8",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
-      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
-      "dev": true,
-      "requires": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      }
-    },
-    "resolve-alpn": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
-      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
-    },
-    "resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true
-    },
-    "resolve-pkg-maps": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
-      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
-      "dev": true
-    },
-    "responselike": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
-      "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
-      "requires": {
-        "lowercase-keys": "^3.0.0"
-      }
-    },
-    "reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true
-    },
-    "rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "devOptional": true,
-      "requires": {
-        "glob": "^7.1.3"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "7.2.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-          "devOptional": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.1.1",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        }
-      }
-    },
-    "run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "requires": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "devOptional": true
-    },
-    "safe-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
-      "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
-      "dev": true,
-      "requires": {
-        "regexp-tree": "~0.1.1"
-      }
-    },
-    "semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
-      "devOptional": true,
-      "requires": {
-        "lru-cache": "^6.0.0"
-      },
-      "dependencies": {
-        "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==",
-          "devOptional": true,
-          "requires": {
-            "yallist": "^4.0.0"
-          }
-        },
-        "yallist": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-          "devOptional": true
-        }
-      }
-    },
-    "serialize-javascript": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
-      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
-      "dev": true,
-      "requires": {
-        "randombytes": "^2.1.0"
-      }
-    },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
-      "devOptional": true
-    },
-    "shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "requires": {
-        "shebang-regex": "^3.0.0"
-      }
-    },
-    "shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true
-    },
-    "signal-exit": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
-      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
-      "dev": true
-    },
-    "sinon": {
-      "version": "17.0.1",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz",
-      "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==",
-      "dev": true,
-      "requires": {
-        "@sinonjs/commons": "^3.0.0",
-        "@sinonjs/fake-timers": "^11.2.2",
-        "@sinonjs/samsam": "^8.0.0",
-        "diff": "^5.1.0",
-        "nise": "^5.1.5",
-        "supports-color": "^7.2.0"
-      },
-      "dependencies": {
-        "diff": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
-          "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
-          "dev": true
-        }
-      }
-    },
-    "sisteransi": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
-      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
-      "dev": true
-    },
-    "source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true
-    },
-    "spawn-sync": {
-      "version": "1.0.15",
-      "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
-      "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==",
-      "dev": true,
-      "requires": {
-        "concat-stream": "^1.4.7",
-        "os-shim": "^0.1.2"
-      }
-    },
-    "spawn-wrap": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
-      "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
-      "dev": true,
-      "requires": {
-        "foreground-child": "^2.0.0",
-        "is-windows": "^1.0.2",
-        "make-dir": "^3.0.0",
-        "rimraf": "^3.0.0",
-        "signal-exit": "^3.0.2",
-        "which": "^2.0.1"
-      },
-      "dependencies": {
-        "foreground-child": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
-          "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
-          "dev": true,
-          "requires": {
-            "cross-spawn": "^7.0.0",
-            "signal-exit": "^3.0.2"
-          }
-        },
-        "signal-exit": {
-          "version": "3.0.7",
-          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-          "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-          "dev": true
-        }
-      }
-    },
-    "sprintf-js": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
-      "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
-    },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "devOptional": true,
-      "requires": {
-        "safe-buffer": "~5.1.0"
-      }
-    },
-    "string-template": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz",
-      "integrity": "sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg==",
-      "optional": true
-    },
-    "string-width": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
-      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
-      "dev": true,
-      "requires": {
-        "eastasianwidth": "^0.2.0",
-        "emoji-regex": "^9.2.2",
-        "strip-ansi": "^7.0.1"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-          "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-          "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^6.0.1"
-          }
-        }
-      }
-    },
-    "string-width-cjs": {
-      "version": "npm:string-width@4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "requires": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "dependencies": {
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        }
-      }
-    },
-    "strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "devOptional": true,
-      "requires": {
-        "ansi-regex": "^5.0.1"
-      }
-    },
-    "strip-ansi-cjs": {
-      "version": "npm:strip-ansi@6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^5.0.1"
-      }
-    },
-    "strip-bom": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
-      "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
-      "dev": true
-    },
-    "strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true
-    },
-    "supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
-      "requires": {
-        "has-flag": "^4.0.0"
-      }
-    },
-    "supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true
-    },
-    "tar": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
-      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
-      "optional": true,
-      "requires": {
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.0.0",
-        "minipass": "^5.0.0",
-        "minizlib": "^2.1.1",
-        "mkdirp": "^1.0.3",
-        "yallist": "^4.0.0"
-      },
-      "dependencies": {
-        "minipass": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
-          "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
-          "optional": true
-        },
-        "yallist": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-          "optional": true
-        }
-      }
-    },
-    "test-exclude": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
-      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
-      "dev": true,
-      "requires": {
-        "@istanbuljs/schema": "^0.1.2",
-        "glob": "^7.1.4",
-        "minimatch": "^3.0.4"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "7.2.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.1.1",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        }
-      }
-    },
-    "text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
-      "dev": true
-    },
-    "to-fast-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "dev": true
-    },
-    "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "requires": {
-        "is-number": "^7.0.0"
-      }
-    },
-    "tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
-      "optional": true
-    },
-    "type-check": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
-      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
-      "dev": true,
-      "requires": {
-        "prelude-ls": "^1.2.1"
-      }
-    },
-    "type-detect": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
-      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
-      "dev": true
-    },
-    "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
-    },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
-      "dev": true
-    },
-    "typedarray-to-buffer": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
-      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
-      "dev": true,
-      "requires": {
-        "is-typedarray": "^1.0.0"
-      }
-    },
-    "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==",
-      "dev": true,
-      "requires": {
-        "escalade": "^3.1.1",
-        "picocolors": "^1.0.0"
-      }
-    },
-    "uri-js": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
-      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
-      "dev": true,
-      "requires": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "devOptional": true
-    },
-    "uuid": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
-      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
-    },
-    "webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
-      "optional": true
-    },
-    "whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "optional": true,
-      "requires": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
-    "which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "requires": {
-        "isexe": "^2.0.0"
-      }
-    },
-    "which-module": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
-      "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
-      "devOptional": true
-    },
-    "wide-align": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
-      "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
-      "optional": true,
-      "requires": {
-        "string-width": "^1.0.2 || 2 || 3 || 4"
-      },
-      "dependencies": {
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "optional": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "optional": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        }
-      }
-    },
-    "workerpool": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
-      "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
-      "dev": true
-    },
-    "wrap-ansi": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
-      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^6.1.0",
-        "string-width": "^5.0.1",
-        "strip-ansi": "^7.0.1"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-          "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "6.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
-          "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-          "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^6.0.1"
-          }
-        }
-      }
-    },
-    "wrap-ansi-cjs": {
-      "version": "npm:wrap-ansi@7.0.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "dependencies": {
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        }
-      }
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "devOptional": true
-    },
-    "write-file-atomic": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
-      "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
-      "dev": true,
-      "requires": {
-        "imurmurhash": "^0.1.4",
-        "is-typedarray": "^1.0.0",
-        "signal-exit": "^3.0.2",
-        "typedarray-to-buffer": "^3.1.5"
-      },
-      "dependencies": {
-        "signal-exit": {
-          "version": "3.0.7",
-          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-          "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-          "dev": true
-        }
-      }
-    },
-    "y18n": {
-      "version": "5.0.8",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
-      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "dev": true
-    },
-    "yallist": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-      "dev": true
-    },
-    "yargs": {
-      "version": "16.2.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
-      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
-      "dev": true,
-      "requires": {
-        "cliui": "^7.0.2",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.0",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^20.2.2"
-      },
-      "dependencies": {
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        }
-      }
-    },
-    "yargs-parser": {
-      "version": "20.2.4",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
-      "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
-      "dev": true
-    },
-    "yargs-unparser": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
-      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^6.0.0",
-        "decamelize": "^4.0.0",
-        "flat": "^5.0.2",
-        "is-plain-obj": "^2.1.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
-          "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
-          "dev": true
-        },
-        "decamelize": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
-          "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
-          "dev": true
-        }
-      }
-    },
-    "yocto-queue": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
-      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
-      "dev": true
-    }
   }
 }
index e0901c785b1f2371aca9aa748b03ddf8717b0164..30cb4b149833b080510e87384af81b3f4873366a 100644 (file)
@@ -1,14 +1,18 @@
 {
   "name": "@squeep/authentication-module",
-  "version": "1.3.2",
-  "description": "Wrangles authenticated sessions; intended for use in Squeep Framework Applications.",
+  "version": "1.5.0",
+  "description": "Wrangles authenticated sessions, endpoints, and resources; intended for use in Squeep Framework Applications.",
   "main": "index.js",
   "scripts": {
+    "audit": "npm audit",
     "coverage": "nyc npm test",
     "coverage-check": "nyc check-coverage",
     "eslint": "eslint index.js lib",
     "test": "mocha --recursive"
   },
+  "files": [
+    "lib/**"
+  ],
   "repository": {
     "type": "git",
     "url": "https://git.squeep.com/squeep-authentication-module/"
     "pam"
   ],
   "engines": {
-    "node": "^18"
+    "node": ">=18"
   },
   "author": "Justin Wind <jwind-npm@squeep.com>",
   "license": "ISC",
   "pre-commit": [
+    "audit",
     "eslint",
     "coverage",
     "coverage-check"
   ],
   "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"
   },
   "optionalDependencies": {
     "argon2": "^0.40.1",
     "node-linux-pam": "^0.2.1"
   },
   "devDependencies": {
-    "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.24.0",
-    "html-validate": "^8.15.0",
-    "mocha": "^10.3.0",
-    "nyc": "^15.1.0",
-    "pre-commit": "^1.2.2",
-    "sinon": "^17.0.1"
+    "@squeep/eslint-config": "^1",
+    "eslint": "^9",
+    "html-validate": "^8",
+    "mocha": "^10",
+    "nyc": "^15",
+    "pre-commit": "^1",
+    "sinon": "^17"
   }
 }
index 534fd02df14165d8029582ab6c3a816b24fbfe9a..75ae9a883547ba055f341a480104959d7fa50b7d 100644 (file)
@@ -1,8 +1,10 @@
 /* eslint-env mocha */
+/* eslint-disable sonarjs/no-duplicate-string */
+/* eslint-disable jsdoc/require-jsdoc */
 'use strict';
 
-const assert = require('assert');
-const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
+const assert = require('node:assert');
+const sinon = require('sinon');
 const Authenticator = require('../../lib/authenticator');
 const stubLogger = require('../stub-logger');
 const stubDb = require('../stub-db');
@@ -73,7 +75,7 @@ describe('Authenticator', function () {
         assert.deepStrictEqual(e, expected);
         assert(authenticator.db.authenticationUpsert.called);
         assert(authenticator.logger.error.called);
-        }
+      }
     });
   }); // createIdentifier
 
@@ -138,13 +140,14 @@ describe('Authenticator', function () {
   }); // _validateAuthDataCredential
 
   describe('isValidBasic', function () {
+    const b64 = (x) => Buffer.from(x).toString('base64');
     it('succeeds', async function () {
       _authMechanismRequired(authenticator, 'argon2');
       authenticator.db.authenticationGet.resolves({
         identifier,
         credential,
       });
-      const authString = `${identifier}:${password}`;
+      const authString = b64(`${identifier}:${password}`);
       const result = await authenticator.isValidBasic(authString, ctx);
       assert.strictEqual(result, true);
       assert.strictEqual(ctx.authenticationId, identifier);
@@ -155,14 +158,14 @@ describe('Authenticator', function () {
         identifier,
         credential,
       });
-      const authString = `${identifier}:wrongPassword}`;
+      const authString = b64(`${identifier}:wrongPassword}`);
       const result = await authenticator.isValidBasic(authString, ctx);
       assert.strictEqual(result, false);
       assert.strictEqual(ctx.authenticationId, undefined);
     });
     it('covers no entry', async function() {
       authenticator.db.authenticationGet.resolves();
-      const authString = `${identifier}:wrongPassword}`;
+      const authString = b64(`${identifier}:wrongPassword}`);
       const result = await authenticator.isValidBasic(authString, ctx);
       assert.strictEqual(result, false);
       assert.strictEqual(ctx.authenticationId, undefined);
@@ -172,7 +175,7 @@ describe('Authenticator', function () {
         identifier,
         credential: '$other$kind_of_credential',
       });
-      const authString = `${identifier}:wrongPassword}`;
+      const authString = b64(`${identifier}:wrongPassword}`);
       const result = await authenticator.isValidBasic(authString, ctx);
       assert.strictEqual(result, false);
       assert.strictEqual(ctx.authenticationId, undefined);
@@ -383,6 +386,22 @@ describe('Authenticator', function () {
     });
   }); // checkOTP
 
+  describe('updateOTPKey', function () {
+    let dbCtx, otpKey;
+    beforeEach(function () {
+      dbCtx = {};
+      otpKey = 'CDBGB3U3B2ILECQORMINGGSZN7LXY565';
+    });
+    it('covers success', async function () {
+      await authenticator.updateOTPKey(dbCtx, identifier, otpKey);
+      assert(authenticator.db.authenticationUpdateOTPKey.called);
+    });
+    it('covers failure', async function () {
+      authenticator.db.authenticationUpdateOTPKey.rejects();
+      assert.rejects(authenticator.updateOTPKey(dbCtx, identifier, otpKey));
+    });
+  }); // updateOTPKey
+
   describe('sessionCheck', function () {
     let req, res, loginPath, required, profilesAllowed;
     beforeEach(function () {
@@ -559,7 +578,7 @@ describe('Authenticator', function () {
     it('covers missing basic auth, ignores session', async function () {
       req.getHeader.returns();
       sinon.stub(authenticator, 'isValidAuthorization').resolves(true);
-      assert.rejects(authenticator.apiRequiredLocal(req, res, ctx, false), {
+      assert.rejects(() => authenticator.apiRequiredLocal(req, res, ctx, false), {
         name: 'ResponseError',
         statusCode: 401,
       });
@@ -567,6 +586,11 @@ describe('Authenticator', function () {
       assert(!authenticator.isValidAuthorization.called);
       assert(res.setHeader.called);
     });
+    it('covers errors', async function () {
+      sinon.stub(authenticator, 'isValidAuthorization').rejects();
+      req.getHeader.returns('Basic Zm9vOmJhcg==');
+      assert.rejects(() => authenticator.apiRequiredLocal(req, res, ctx));
+    });
   }); // apiRequiredLocal
 
 }); // Authenticator
index 82b80f0db7a18f7e1ef35b2b13bccdec18d671a0..5ddc3d752a4c3b0778062a4da2e886019f6831f1 100644 (file)
@@ -1,7 +1,7 @@
 /* eslint-env mocha */
 'use strict';
 
-const assert = require('assert');
+const assert = require('node:assert');
 const sinon = require('sinon');
 const stubLogger = require('../stub-logger');
 const common = require('../../lib/common');
diff --git a/test/lib/resource-authenticator.js b/test/lib/resource-authenticator.js
new file mode 100644 (file)
index 0000000..7f360e3
--- /dev/null
@@ -0,0 +1,239 @@
+'use strict';
+
+const assert = require('node:assert');
+const sinon = require('sinon');
+const ResourceAuthenticator = require('../../lib/resource-authenticator');
+const stubLogger = require('../stub-logger');
+const stubDb = require('../stub-db');
+const Config = require('../stub-config');
+const { ResponseError, ResourceAuthenticatorError } = require('../../lib/errors');
+const Enum = require('../../lib/enum');
+
+describe('Resource Authenticator', function () {
+  const noExpectedException = 'did not receive expected exception';
+  let ra, options;
+
+  beforeEach(function () {
+    stubDb._reset();
+    stubLogger._reset();
+    options = new Config('test');
+    ra = new ResourceAuthenticator(stubLogger, stubDb, options);
+  });
+
+  afterEach(function () {
+    sinon.restore();
+  });
+
+  it('covers no option default', function () {
+    ra = new ResourceAuthenticator(stubLogger, stubDb);
+  });
+
+  describe('currentEpoch', function () {
+    it('covers', function () {
+      const now = 1648836413503;
+      const expected = Math.ceil(now / 1000);
+      sinon.stub(Date, 'now').returns(now);
+      const result = ResourceAuthenticator.currentEpoch;
+      assert.strictEqual(result, expected);
+    });
+  }); // currentEpoch
+
+  describe('Identifier Compaction', function () {
+    it('reciprocates', function () {
+      const identifier = '6eaed948-b1e4-11ec-9a91-0025905f714a';
+      const smaller = ResourceAuthenticator.ensmallenIdentifier(identifier);
+      const bigger = ResourceAuthenticator.embiggenIdentifier(smaller);
+      assert.strictEqual(bigger, identifier);
+    });
+  }); // Identifier Compaction
+
+  describe('getSalt', function () {
+    it('covers', async function () {
+      const result = await ra.getSalt();
+      assert(result);
+      assert.strictEqual(result.length, 28);
+    });
+  }); // getSalt
+
+  describe('createDigest', function () {
+    let secret;
+    beforeEach(function () {
+      secret = 'secret';
+    });
+    it('creates empty digest', function () {
+      const result = ra.createDigest(secret);
+      const expected = '-eZuF5tnR65UEI-C-K3os8Jddv0wr95sOVgixTAZYWk';
+      assert.strictEqual(result, expected);
+    });
+    it('creates digest', function () {
+      const result = ra.createDigest(secret, 'data');
+      const expected = 'GywWt1vSqHDBFBU8zaW8_KYzFLxyL6Fg1pDeEzzLuds';
+      assert.strictEqual(result, expected);
+    });
+  }); // createDigest
+
+  describe('authenticate', function () {
+    it('covers', async function () {
+      const identifier = '6eaed948-b1e4-11ec-9a91-0025905f714a';
+      const secret = 'secrety';
+      sinon.stub(ResourceAuthenticator, 'currentEpoch').get(() => 1648836029);
+      sinon.stub(ra, 'getSalt').resolves('xxxxx');
+      const expected = 'Bearer bq7ZSLHkEeyakQAlkF9xSg:1648836029:xxxxx:fdUYC8Gqe0nAyX_-SWvRsPsx0UjY-vV-Ff0A52j6Zfw';
+      const result = await ra.authenticate(identifier, secret);
+      assert.strictEqual(result, expected);
+    });
+  }); // authenticate
+
+  describe('required', function () {
+    let resource, res, req, ctx;
+    const validBearerHeader = 'Bearer bq7ZSLHkEeyakQAlkF9xSg:1648836029:xxxxx:fdUYC8Gqe0nAyX_-SWvRsPsx0UjY-vV-Ff0A52j6Zfw';
+    beforeEach(function () {
+      resource = {
+        secret: 'secrety',
+      };
+      req = {
+        getHeader: sinon.stub(),
+      };
+      res = {
+        setHeader: sinon.stub(),
+      };
+      ctx = {
+        params: {},
+        parsedBody: {},
+        queryParams: {},
+        session: {},
+      };  
+    });
+    it('requires auth header', async function () {
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('requires bearer token', async function () {
+      req.getHeader.returns('Basic Zm9vcABiYXJr');
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('requires proper bearer token', async function () {
+      req.getHeader.returns('Bearer Zm9vcABiYXJr');
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('requires identifier to exist', async function () {
+      req.getHeader.returns(validBearerHeader);
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('covers db failure', async function () {
+      const expected = new Error('oh no');
+      ra.db.resourceGet.rejects(expected);
+      req.getHeader.returns(validBearerHeader);
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert.deepStrictEqual(e, expected, noExpectedException);
+      }
+    });
+    it('requires timestamp within grace', async function () {
+      sinon.stub(ResourceAuthenticator, 'currentEpoch').get(() => 1648838184);
+      ra.db.resourceGet.resolves(resource);
+      req.getHeader.returns(validBearerHeader);
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('requires digest to match', async function () {
+      sinon.stub(ResourceAuthenticator, 'currentEpoch').get(() => 1648836031);
+      ra.db.resourceGet.resolves(resource);
+      req.getHeader.returns('Bearer bq7ZSLHkEeyakQAlkF9xSg:1648836029:xxxxx:invalid1M2j9wtoerc3Pqe6kRzqFrkrkwqdeYXG331Q');
+      try {
+        await ra.required(req, res, ctx);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('succeeds', async function () {
+      sinon.stub(ResourceAuthenticator, 'currentEpoch').get(() => 1648836031);
+      ra.db.resourceGet.resolves(resource);
+      req.getHeader.returns(validBearerHeader);
+      await ra.required(req, res, ctx);
+      assert.strictEqual(ra.logger.debug.args[1][1], 'success');
+    });
+    it('covers extra bearer token fields', async function () {
+      sinon.stub(ResourceAuthenticator, 'currentEpoch').get(() => 1648836031);
+      ra.db.resourceGet.resolves(resource);
+      req.getHeader.returns(validBearerHeader + ':extra');
+      await ra.required(req, res, ctx);
+      assert.deepStrictEqual(ra.logger.debug.args[1][2], { tRest: ['extra'] });
+      assert.strictEqual(ra.logger.debug.args[2][1], 'success');
+    });
+  }); // required
+
+  describe('requestBearer', function () {
+    let res;
+    beforeEach(function () {
+      res = {
+        setHeader: sinon.stub(),
+      };
+    });
+    it('covers default response', function () {
+      try {
+        ResourceAuthenticator.requestBearer(res);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(res.setHeader.called);
+        assert.strictEqual(res.setHeader.args[0][0], 'WWW-Authenticate');
+        assert.strictEqual(res.setHeader.args[0][1], 'Bearer');
+        assert(e instanceof ResponseError, noExpectedException);
+        assert.strictEqual(e.statusCode, 401);
+      }
+    });
+    it('covers other response', function () {
+      try {
+        ResourceAuthenticator.requestBearer(res, Enum.ErrorResponse.Forbidden);
+        assert.fail(noExpectedException);
+      } catch (e) {
+        assert(res.setHeader.called);
+        assert.strictEqual(res.setHeader.args[0][0], 'WWW-Authenticate');
+        assert.strictEqual(res.setHeader.args[0][1], 'Bearer');
+        assert(e instanceof ResponseError);
+        assert.strictEqual(e.statusCode, 403);
+      }
+    });
+  }); // requestBearer
+
+  describe('ResourceAuthenticatorError', function () {
+    it('covers', function () {
+      const e = new ResourceAuthenticatorError();
+      const result = e.name;
+      assert.strictEqual(result, 'ResourceAuthenticatorError');
+    });
+  }); // ResourceAuthenticationError
+
+}); // Resource Authenticator
\ No newline at end of file
index f0aca6d8e339ad1329d125c340ae47dd4425dea6..16243d732daf04ae6ea7400f137a35752e0af044 100644 (file)
@@ -1,10 +1,10 @@
 /* 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 assert = require('node:assert');
+const sinon = require('sinon');
 
 const SessionManager = require('../../lib/session-manager');
 const Enum = require('../../lib/enum');
@@ -81,7 +81,7 @@ describe('SessionManager', function () {
     it('covers', async function () {
       await manager._sessionCookieClear(res);
       assert(res.appendHeader.called);
-    })
+    });
   }); // _sessionCookieClear
 
   describe('getAdminLogin', function () {
@@ -406,7 +406,7 @@ describe('SessionManager', function () {
     beforeEach(function () {
       state = '4ea7e936-3427-11ec-9f4b-0025905f714a';
       me = 'https://example.com/profile';
-      authorizationEndpoint = 'https://example.com/auth'
+      authorizationEndpoint = 'https://example.com/auth';
       ctx.cookie = {
         squeepSession: 'sessionCookie',
       };
index 54865cb2e5c0e9d3eded6542e4e3354b425b1fa5..c0dbbb682ad8ec320d862255ee4340db9d269a62 100644 (file)
@@ -9,6 +9,7 @@ describe('stdioCredential', function () {
   let answerCb, prompt;
 
   beforeEach(function () {
+    // eslint-disable-next-line no-unused-vars
     sinon.stub(readline, 'createInterface').callsFake(({ input, output, terminal }) => {
       return {
         close: () => undefined,
index 686f72bab6c97e48293f4b3f7f234d56851ba0b7..fb57bb9e334b71fff6e86aa1ccb22f36468c76f9 100644 (file)
@@ -11,7 +11,7 @@ describe('Template Helpers', function () {
       ctx = {
         session: {
           authenticatedIdentifier: 'username',
-        }
+        },
       };
       options = {};
     });
index 87bdf0a7070ff9be3f7d81a0224ecf4cf449d5f8..aabb9ace64c7ab3de414af804c12ed993168edbd 100644 (file)
@@ -1,7 +1,7 @@
 /* eslint-env mocha */
 'use strict';
 
-const assert = require('assert');
+const assert = require('node:assert');
 const { IAHTML } = require('../../../lib/template');
 const lintHtml = require('../../lint-html');
 
index 04e755584d6ee7c30584ea166b7f20963946b2dd..44d8fbbf250c797a417c542407dabffb812c8c13 100644 (file)
@@ -1,7 +1,7 @@
 /* eslint-env mocha */
 'use strict';
 
-const assert = require('assert');
+const assert = require('node:assert');
 const { LoginHTML } = require('../../../lib/template');
 const lintHtml = require('../../lint-html');
 
index 0e84a4950055ae59a1e2d547ef7f45bb8155a16d..2a49a71959245af873001d058b1dcae5c5f657c2 100644 (file)
@@ -1,7 +1,7 @@
 /* eslint-env mocha */
 'use strict';
 
-const assert = require('assert');
+const assert = require('node:assert');
 const { OTPHTML } = require('../../../lib/template');
 const lintHtml = require('../../lint-html');
 
index 858f1c22435264a0f60d8e4ce9455e444812d2dd..e4ba5295efaef9929035801bff8b5b5302594154 100644 (file)
@@ -1,9 +1,8 @@
 /* eslint-env mocha */
 'use strict';
 
-const assert = require('assert');
+const assert = require('node:assert');
 const { SettingsHTML } = require('../../../lib/template');
-const stubLogger = require('../../stub-logger');
 const lintHtml = require('../../lint-html');
 
 describe('Template SettingsHTML', function () {
@@ -25,7 +24,7 @@ describe('Template SettingsHTML', function () {
 
   it('renders, no otp', async function () {
     ctx.errors = ['an error', 'another error'];
-    ctx.notifications = ['a notice']
+    ctx.notifications = ['a notice'];
     const result = SettingsHTML(ctx, options);
     await lintHtml(result);
     assert(result);
index 733dd0020707c283fba95b02e826f0c2a0af2441..c1b279cd899fb2e913b2128e9270660593dd5361 100644 (file)
@@ -1,3 +1,4 @@
+/* eslint-disable jsdoc/require-jsdoc */
 'use strict';
 
 function Config() {
index 0df6b9ebb097d225304e8d699418edf2afdf20ff..537c92b88859a7a40c104032aafe8965f72b363c 100644 (file)
@@ -1,7 +1,7 @@
 /* eslint-disable security/detect-object-injection */
 'use strict';
 
-const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
+const sinon = require('sinon');
 
 const spyFns = [
   'context',
@@ -14,6 +14,7 @@ const stubFns = [
   'authenticationSuccess',
   'authenticationUpdateCredential',
   'authenticationUpdateOTPKey',
+  'resourceGet',
 ];
 
 const stubDatabase = {
index 0293fb0bd96b45b37a06c6182b95c247f11579d1..a6e787e89a1dd68cc509a190d5859d1e0d8c3874 100644 (file)
@@ -1,6 +1,6 @@
 'use strict';
 
-const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
+const sinon = require('sinon');
 
 const stubLogger = process.env.VERBOSE_TESTS ? console : {
   debug: () => undefined,